diff --git a/.arcconfig b/.arcconfig deleted file mode 100644 index 548af935888..00000000000 --- a/.arcconfig +++ /dev/null @@ -1,8 +0,0 @@ -{ - "project_id" : "Blender", - "conduit_uri" : "https://developer.blender.org/", - "phabricator.uri" : "https://developer.blender.org/", - "git.default-relative-commit" : "origin/master", - "arc.land.update.default" : "rebase", - "arc.land.onto.default" : "master" -} diff --git a/.gitea/issue_template/bug.yaml b/.gitea/issue_template/bug.yaml index 4e3c550dae9..71cb6bebd55 100644 --- a/.gitea/issue_template/bug.yaml +++ b/.gitea/issue_template/bug.yaml @@ -1,9 +1,9 @@ name: Bug Report about: File a bug report labels: - - "type::Report" - - "status::Needs Triage" - - "priority::Normal" + - "Type/Report" + - "Status/Needs Triage" + - "Priority/Normal" body: - type: markdown attributes: diff --git a/.gitea/issue_template/design.yaml b/.gitea/issue_template/design.yaml index a1dcd8b0eda..77195a8c79a 100644 --- a/.gitea/issue_template/design.yaml +++ b/.gitea/issue_template/design.yaml @@ -1,7 +1,7 @@ name: Design about: Create a design task (for developers only) labels: - - "type::Design" + - "Type/Design" body: - type: textarea id: body diff --git a/.gitea/issue_template/todo.yaml b/.gitea/issue_template/todo.yaml index 58e848c3e18..325238afc20 100644 --- a/.gitea/issue_template/todo.yaml +++ b/.gitea/issue_template/todo.yaml @@ -1,7 +1,7 @@ name: To Do about: Create a to do task (for developers only) labels: - - "type::To Do" + - "Type/To Do" body: - type: textarea id: body diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index b5da14a63d1..dab4417109a 100644 --- a/build_files/build_environment/cmake/download.cmake +++ b/build_files/build_environment/cmake/download.cmake @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later ## Update and uncomment this in the release branch -set(BLENDER_VERSION 3.5) +# set(BLENDER_VERSION 3.1) function(download_source dep) set(TARGET_FILE ${${dep}_FILE}) diff --git a/build_files/build_environment/cmake/epoxy.cmake b/build_files/build_environment/cmake/epoxy.cmake index da9c204d5ec..28dd8a70f26 100644 --- a/build_files/build_environment/cmake/epoxy.cmake +++ b/build_files/build_environment/cmake/epoxy.cmake @@ -10,7 +10,7 @@ ExternalProject_Add(external_epoxy URL_HASH ${EPOXY_HASH_TYPE}=${EPOXY_HASH} PREFIX ${BUILD_DIR}/epoxy PATCH_COMMAND ${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/epoxy/src/external_epoxy/ < ${PATCH_DIR}/epoxy.diff - CONFIGURE_COMMAND ${CONFIGURE_ENV} && ${MESON} setup --prefix ${LIBDIR}/epoxy --default-library ${EPOXY_LIB_TYPE} --libdir lib ${BUILD_DIR}/epoxy/src/external_epoxy-build ${BUILD_DIR}/epoxy/src/external_epoxy -Dtests=false + CONFIGURE_COMMAND ${CONFIGURE_ENV} && ${MESON} setup --prefix ${LIBDIR}/epoxy --default-library ${EPOXY_LIB_TYPE} --libdir lib ${BUILD_DIR}/epoxy/src/external_epoxy-build ${BUILD_DIR}/epoxy/src/external_epoxy -Dtests=false ${MESON_BUILD_TYPE} BUILD_COMMAND ninja INSTALL_COMMAND ninja install ) diff --git a/build_files/build_environment/cmake/fribidi.cmake b/build_files/build_environment/cmake/fribidi.cmake index 9d83191741f..e1dd6287d12 100644 --- a/build_files/build_environment/cmake/fribidi.cmake +++ b/build_files/build_environment/cmake/fribidi.cmake @@ -9,7 +9,7 @@ ExternalProject_Add(external_fribidi URL_HASH ${FRIBIDI_HASH_TYPE}=${FRIBIDI_HASH} DOWNLOAD_DIR ${DOWNLOAD_DIR} PREFIX ${BUILD_DIR}/fribidi - CONFIGURE_COMMAND ${MESON} setup --prefix ${LIBDIR}/fribidi -Ddocs=false --default-library static --libdir lib ${BUILD_DIR}/fribidi/src/external_fribidi-build ${BUILD_DIR}/fribidi/src/external_fribidi + CONFIGURE_COMMAND ${MESON} setup --prefix ${LIBDIR}/fribidi ${MESON_BUILD_TYPE} -Ddocs=false --default-library static --libdir lib ${BUILD_DIR}/fribidi/src/external_fribidi-build ${BUILD_DIR}/fribidi/src/external_fribidi BUILD_COMMAND ninja INSTALL_COMMAND ninja install INSTALL_DIR ${LIBDIR}/fribidi diff --git a/build_files/build_environment/cmake/harfbuzz.cmake b/build_files/build_environment/cmake/harfbuzz.cmake index 5f57ab6c0ff..67dccf487b3 100644 --- a/build_files/build_environment/cmake/harfbuzz.cmake +++ b/build_files/build_environment/cmake/harfbuzz.cmake @@ -21,6 +21,7 @@ set(HARFBUZZ_EXTRA_OPTIONS # Only used for command line utilities, # disable as this would add an addition & unnecessary build-dependency. -Dcairo=disabled + ${MESON_BUILD_TYPE} ) ExternalProject_Add(external_harfbuzz @@ -59,3 +60,10 @@ if(BUILD_MODE STREQUAL Release AND WIN32) DEPENDEES install ) endif() + +if(BUILD_MODE STREQUAL Debug AND WIN32) + ExternalProject_Add_Step(external_harfbuzz after_install + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/harfbuzz/lib/libharfbuzz.a ${HARVEST_TARGET}/harfbuzz/lib/libharfbuzz_d.lib + DEPENDEES install + ) +endif() diff --git a/build_files/build_environment/cmake/mesa.cmake b/build_files/build_environment/cmake/mesa.cmake index ab07c09cc00..c60acaaaa35 100644 --- a/build_files/build_environment/cmake/mesa.cmake +++ b/build_files/build_environment/cmake/mesa.cmake @@ -15,7 +15,7 @@ llvm-config = '${LIBDIR}/llvm/bin/llvm-config'" ) set(MESA_EXTRA_FLAGS - -Dbuildtype=release + ${MESON_BUILD_TYPE} -Dc_args=${MESA_CFLAGS} -Dcpp_args=${MESA_CXXFLAGS} -Dc_link_args=${MESA_LDFLAGS} diff --git a/build_files/build_environment/cmake/openvdb.cmake b/build_files/build_environment/cmake/openvdb.cmake index bd008287f75..8fa0bbc11ea 100644 --- a/build_files/build_environment/cmake/openvdb.cmake +++ b/build_files/build_environment/cmake/openvdb.cmake @@ -44,13 +44,21 @@ set(OPENVDB_EXTRA_ARGS # -DLLVM_DIR=${LIBDIR}/llvm/lib/cmake/llvm ) +set(OPENVDB_PATCH ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/openvdb/src/openvdb < ${PATCH_DIR}/openvdb.diff) +if(APPLE) + set(OPENVDB_PATCH + ${OPENVDB_PATCH} && + ${PATCH_CMD} -p 0 -d ${BUILD_DIR}/openvdb/src/openvdb < ${PATCH_DIR}/openvdb_metal.diff + ) +endif() + ExternalProject_Add(openvdb URL file://${PACKAGE_DIR}/${OPENVDB_FILE} DOWNLOAD_DIR ${DOWNLOAD_DIR} URL_HASH ${OPENVDB_HASH_TYPE}=${OPENVDB_HASH} CMAKE_GENERATOR ${PLATFORM_ALT_GENERATOR} PREFIX ${BUILD_DIR}/openvdb - PATCH_COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/openvdb/src/openvdb < ${PATCH_DIR}/openvdb.diff + PATCH_COMMAND ${OPENVDB_PATCH} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openvdb ${DEFAULT_CMAKE_FLAGS} ${OPENVDB_EXTRA_ARGS} INSTALL_DIR ${LIBDIR}/openvdb ) diff --git a/build_files/build_environment/cmake/options.cmake b/build_files/build_environment/cmake/options.cmake index 0a7548f95fb..34861caf8fc 100644 --- a/build_files/build_environment/cmake/options.cmake +++ b/build_files/build_environment/cmake/options.cmake @@ -16,8 +16,10 @@ message("BuildMode = ${BUILD_MODE}") if(BUILD_MODE STREQUAL "Debug") set(LIBDIR ${CMAKE_CURRENT_BINARY_DIR}/Debug) + set(MESON_BUILD_TYPE -Dbuildtype=debug) else() set(LIBDIR ${CMAKE_CURRENT_BINARY_DIR}/Release) + set(MESON_BUILD_TYPE -Dbuildtype=release) endif() set(DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/downloads" CACHE STRING "Path for downloaded files") diff --git a/build_files/build_environment/cmake/wayland.cmake b/build_files/build_environment/cmake/wayland.cmake index ab573a8dca9..4160de2bfa8 100644 --- a/build_files/build_environment/cmake/wayland.cmake +++ b/build_files/build_environment/cmake/wayland.cmake @@ -13,7 +13,7 @@ ExternalProject_Add(external_wayland # NOTE: `-lm` is needed for `libxml2` which is a static library that uses `libm.so`, # without this, math symbols such as `floor` aren't found. CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env PKG_CONFIG_PATH=${LIBDIR}/expat/lib/pkgconfig:${LIBDIR}/xml2/lib/pkgconfig:${LIBDIR}/ffi/lib/pkgconfig:$PKG_CONFIG_PATH - ${MESON} --prefix ${LIBDIR}/wayland -Ddocumentation=false -Dtests=false -D "c_link_args=-L${LIBDIR}/ffi/lib -lm" . ../external_wayland + ${MESON} --prefix ${LIBDIR}/wayland ${MESON_BUILD_TYPE} -Ddocumentation=false -Dtests=false -D "c_link_args=-L${LIBDIR}/ffi/lib -lm" . ../external_wayland BUILD_COMMAND ninja INSTALL_COMMAND ninja install ) diff --git a/build_files/build_environment/cmake/wayland_protocols.cmake b/build_files/build_environment/cmake/wayland_protocols.cmake index 708d9adba1e..b588419df01 100644 --- a/build_files/build_environment/cmake/wayland_protocols.cmake +++ b/build_files/build_environment/cmake/wayland_protocols.cmake @@ -7,7 +7,7 @@ ExternalProject_Add(external_wayland_protocols PREFIX ${BUILD_DIR}/wayland-protocols # Use `-E` so the `PKG_CONFIG_PATH` can be defined to link against our own WAYLAND. CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env PKG_CONFIG_PATH=${LIBDIR}/wayland/lib64/pkgconfig:$PKG_CONFIG_PATH - ${MESON} --prefix ${LIBDIR}/wayland-protocols . ../external_wayland_protocols -Dtests=false + ${MESON} --prefix ${LIBDIR}/wayland-protocols ${MESON_BUILD_TYPE} . ../external_wayland_protocols -Dtests=false BUILD_COMMAND ninja INSTALL_COMMAND ninja install ) diff --git a/build_files/build_environment/cmake/xvidcore.cmake b/build_files/build_environment/cmake/xvidcore.cmake index f64d00f4c4a..e319408ca15 100644 --- a/build_files/build_environment/cmake/xvidcore.cmake +++ b/build_files/build_environment/cmake/xvidcore.cmake @@ -17,11 +17,13 @@ ExternalProject_Add(external_xvidcore INSTALL_DIR ${LIBDIR}/xvidcore ) -ExternalProject_Add_Step(external_xvidcore after_install - COMMAND ${CMAKE_COMMAND} -E rename ${LIBDIR}/xvidcore/lib/xvidcore.a ${LIBDIR}/xvidcore/lib/libxvidcore.a || true - COMMAND ${CMAKE_COMMAND} -E remove ${LIBDIR}/xvidcore/lib/xvidcore.dll.a - DEPENDEES install -) +if(WIN32) + ExternalProject_Add_Step(external_xvidcore after_install + COMMAND ${CMAKE_COMMAND} -E rename ${LIBDIR}/xvidcore/lib/xvidcore.a ${LIBDIR}/xvidcore/lib/libxvidcore.a || true + COMMAND ${CMAKE_COMMAND} -E remove ${LIBDIR}/xvidcore/lib/xvidcore.dll.a + DEPENDEES install + ) +endif() if(MSVC) set_target_properties(external_xvidcore PROPERTIES FOLDER Mingw) diff --git a/build_files/build_environment/patches/openvdb_metal.diff b/build_files/build_environment/patches/openvdb_metal.diff new file mode 100644 index 00000000000..6a1e723a9af --- /dev/null +++ b/build_files/build_environment/patches/openvdb_metal.diff @@ -0,0 +1,8007 @@ +Index: nanovdb/nanovdb/NanoVDB.h +=================================================================== +--- nanovdb/nanovdb/NanoVDB.h (revision 63215) ++++ nanovdb/nanovdb/NanoVDB.h (working copy) +@@ -140,8 +140,28 @@ + #define NANOVDB_ALIGN(n) alignas(n) + #endif // !defined(NANOVDB_ALIGN) + +-#ifdef __CUDACC_RTC__ ++#ifdef __KERNEL_METAL__ + ++using namespace metal; ++#define std metal ++#define double uint64_t ++#define __global__ device ++#define __local__ thread ++#define __constant__ constant ++#define sqrtf sqrt ++#define rintf rint ++#define fminf fmin ++#define fmaxf fmax ++#define floorf floor ++#define ceilf ceil ++#define fabs abs ++#define fmaf fma ++#define tanf tan ++ ++#define NANOVDB_ASSERT(x) ++ ++#elif defined(__CUDACC_RTC__) ++ + typedef signed char int8_t; + typedef short int16_t; + typedef int int32_t; +@@ -157,6 +177,10 @@ + + #else // !__CUDACC_RTC__ + ++#define __constant__ const ++#define __global__ ++#define __local__ ++ + #include // for abs in clang7 + #include // for types like int32_t etc + #include // for size_t type +@@ -262,7 +286,7 @@ + Index = 19,// index into an external array of values + End = 20 }; + +-#ifndef __CUDACC_RTC__ ++#if !defined(__CUDACC_RTC__) && !defined(__KERNEL_METAL__) + /// @brief Retuns a c-string used to describe a GridType + inline const char* toStr(GridType gridType) + { +@@ -289,7 +313,7 @@ + IndexGrid = 8,// grid whose values are offsets, e.g. into an external array + End = 9 }; + +-#ifndef __CUDACC_RTC__ ++#if !defined(__CUDACC_RTC__) && !defined(__KERNEL_METAL__) + /// @brief Retuns a c-string used to describe a GridClass + inline const char* toStr(GridClass gridClass) + { +@@ -313,7 +337,7 @@ + End = 1 << 6, + }; + +-#ifndef __CUDACC_RTC__ ++#if !defined(__CUDACC_RTC__) && !defined(__KERNEL_METAL__) + /// @brief Retuns a c-string used to describe a GridFlags + inline const char* toStr(GridFlags gridFlags) + { +@@ -355,13 +379,13 @@ + template + struct is_same + { +- static constexpr bool value = false; ++ static __constant__ constexpr bool value = false; + }; + + template + struct is_same + { +- static constexpr bool value = true; ++ static __constant__ constexpr bool value = true; + }; + + // --------------------------> enable_if <------------------------------------ +@@ -383,13 +407,13 @@ + template + struct is_const + { +- static constexpr bool value = false; ++ static __constant__ constexpr bool value = false; + }; + + template + struct is_const + { +- static constexpr bool value = true; ++ static __constant__ constexpr bool value = true; + }; + + // --------------------------> remove_const <------------------------------------ +@@ -412,7 +436,7 @@ + template + struct is_floating_point + { +- static const bool value = is_same::value || is_same::value; ++ static __constant__ const bool value = is_same::value || is_same::value; + }; + + // --------------------------> is_specialization <------------------------------------ +@@ -425,12 +449,12 @@ + template class TemplateType> + struct is_specialization + { +- static const bool value = false; ++ static __constant__ const bool value = false; + }; + template class TemplateType> + struct is_specialization, TemplateType> + { +- static const bool value = true; ++ static __constant__ const bool value = true; + }; + + // --------------------------> Value Map <------------------------------------ +@@ -495,19 +519,19 @@ + // --------------------------> utility functions related to alignment <------------------------------------ + + /// @brief return true if the specified pointer is aligned +-__hostdev__ inline static bool isAligned(const void* p) ++__hostdev__ inline static bool isAligned(__global__ const void* p) + { + return uint64_t(p) % NANOVDB_DATA_ALIGNMENT == 0; + } + + /// @brief return true if the specified pointer is aligned and not NULL +-__hostdev__ inline static bool isValid(const void* p) ++__hostdev__ inline static bool isValid(__global__ const void* p) + { + return p != nullptr && uint64_t(p) % NANOVDB_DATA_ALIGNMENT == 0; + } + + /// @brief return the smallest number of bytes that when added to the specified pointer results in an aligned pointer +-__hostdev__ inline static uint64_t alignmentPadding(const void* p) ++__hostdev__ inline static uint64_t alignmentPadding(__global__ const void* p) + { + NANOVDB_ASSERT(p); + return (NANOVDB_DATA_ALIGNMENT - (uint64_t(p) % NANOVDB_DATA_ALIGNMENT)) % NANOVDB_DATA_ALIGNMENT; +@@ -515,43 +539,66 @@ + + /// @brief offset the specified pointer so it is aligned. + template +-__hostdev__ inline static T* alignPtr(T* p) ++__hostdev__ inline static __global__ T* alignPtr(__global__ T* p) + { + NANOVDB_ASSERT(p); +- return reinterpret_cast( (uint8_t*)p + alignmentPadding(p) ); ++ return reinterpret_cast<__global__ T*>( (__global__ uint8_t*)p + alignmentPadding(p) ); + } + + /// @brief offset the specified pointer so it is aligned. + template +-__hostdev__ inline static const T* alignPtr(const T* p) ++__hostdev__ inline static __global__ const T* alignPtr(__global__ const T* p) + { + NANOVDB_ASSERT(p); +- return reinterpret_cast( (const uint8_t*)p + alignmentPadding(p) ); ++ return reinterpret_cast<__global__ const T*>( (__global__ const uint8_t*)p + alignmentPadding(p) ); + } + + // --------------------------> PtrDiff PtrAdd <------------------------------------ + + template +-__hostdev__ inline static int64_t PtrDiff(const T1* p, const T2* q) ++__hostdev__ inline static int64_t PtrDiff(__global__ const T1* p, __global__ const T2* q) + { + NANOVDB_ASSERT(p && q); +- return reinterpret_cast(p) - reinterpret_cast(q); ++ return reinterpret_cast<__global__ const char*>(p) - reinterpret_cast<__global__ const char*>(q); + } ++#if defined(__KERNEL_METAL__) ++template ++__hostdev__ inline static int64_t PtrDiff(__local__ const T1* p, __local__ const T2* q) ++{ ++ NANOVDB_ASSERT(p && q); ++ return reinterpret_cast<__local__ const char*>(p) - reinterpret_cast<__local__ const char*>(q); ++} ++#endif + + template +-__hostdev__ inline static DstT* PtrAdd(SrcT *p, int64_t offset) ++__hostdev__ inline static __global__ DstT* PtrAdd(__global__ SrcT *p, int64_t offset) + { + NANOVDB_ASSERT(p); +- return reinterpret_cast(reinterpret_cast(p) + offset); ++ return reinterpret_cast<__global__ DstT*>(reinterpret_cast<__global__ char*>(p) + offset); + } ++#if defined(__KERNEL_METAL__) ++template ++__hostdev__ inline static __local__ DstT* PtrAdd(__local__ SrcT *p, int64_t offset) ++{ ++ NANOVDB_ASSERT(p); ++ return reinterpret_cast<__local__ DstT*>(reinterpret_cast<__local__ char*>(p) + offset); ++} ++#endif + + template +-__hostdev__ inline static const DstT* PtrAdd(const SrcT *p, int64_t offset) ++__hostdev__ inline static __global__ const DstT* PtrAdd(__global__ const SrcT *p, int64_t offset) + { + NANOVDB_ASSERT(p); +- return reinterpret_cast(reinterpret_cast(p) + offset); ++ return reinterpret_cast<__global__ const DstT*>(reinterpret_cast<__global__ const char*>(p) + offset); + } +- ++#if defined(__KERNEL_METAL__) ++template ++__hostdev__ inline static __local__ const DstT* PtrAdd(__local__ const SrcT *p, int64_t offset) ++{ ++ NANOVDB_ASSERT(p); ++ return reinterpret_cast<__local__ const DstT*>(reinterpret_cast<__local__ const char*>(p) + offset); ++} ++#endif + // --------------------------> Rgba8 <------------------------------------ + + /// @brief 8-bit red, green, blue, alpha packed into 32 bit unsigned int +@@ -562,13 +609,13 @@ + uint32_t packed;// 32 bit packed representation + } mData; + public: +- static const int SIZE = 4; ++ static __constant__ const int SIZE = 4; + using ValueType = uint8_t; + +- Rgba8(const Rgba8&) = default; +- Rgba8(Rgba8&&) = default; +- Rgba8& operator=(Rgba8&&) = default; +- Rgba8& operator=(const Rgba8&) = default; ++ Rgba8(__global__ const Rgba8&) = default; ++ Rgba8(__global__ Rgba8&&) = default; ++ __global__ Rgba8& operator=(__global__ Rgba8&&) __global__ = default; ++ __global__ Rgba8& operator=(__global__ const Rgba8&) __global__ = default; + __hostdev__ Rgba8() : mData{0,0,0,0} {static_assert(sizeof(uint32_t) == sizeof(Rgba8),"Unexpected sizeof");} + __hostdev__ Rgba8(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255u) : mData{r, g, b, a} {} + explicit __hostdev__ Rgba8(uint8_t v) : Rgba8(v,v,v,v) {} +@@ -579,8 +626,8 @@ + (uint8_t(0.5f + a * 255.0f))}// round to nearest + { + } +- __hostdev__ bool operator<(const Rgba8& rhs) const { return mData.packed < rhs.mData.packed; } +- __hostdev__ bool operator==(const Rgba8& rhs) const { return mData.packed == rhs.mData.packed; } ++ __hostdev__ bool operator<(__global__ const Rgba8& rhs) const { return mData.packed < rhs.mData.packed; } ++ __hostdev__ bool operator==(__global__ const Rgba8& rhs) const { return mData.packed == rhs.mData.packed; } + __hostdev__ float lengthSqr() const + { + return 0.0000153787005f*(float(mData.c[0])*mData.c[0] + +@@ -588,18 +635,18 @@ + float(mData.c[2])*mData.c[2]);//1/255^2 + } + __hostdev__ float length() const { return sqrtf(this->lengthSqr() ); } +- __hostdev__ const uint8_t& operator[](int n) const { return mData.c[n]; } +- __hostdev__ uint8_t& operator[](int n) { return mData.c[n]; } +- __hostdev__ const uint32_t& packed() const { return mData.packed; } +- __hostdev__ uint32_t& packed() { return mData.packed; } +- __hostdev__ const uint8_t& r() const { return mData.c[0]; } +- __hostdev__ const uint8_t& g() const { return mData.c[1]; } +- __hostdev__ const uint8_t& b() const { return mData.c[2]; } +- __hostdev__ const uint8_t& a() const { return mData.c[3]; } +- __hostdev__ uint8_t& r() { return mData.c[0]; } +- __hostdev__ uint8_t& g() { return mData.c[1]; } +- __hostdev__ uint8_t& b() { return mData.c[2]; } +- __hostdev__ uint8_t& a() { return mData.c[3]; } ++ __hostdev__ __global__ const uint8_t& operator[](int n) const __global__ { return mData.c[n]; } ++ __hostdev__ __global__ uint8_t& operator[](int n) __global__ { return mData.c[n]; } ++ __hostdev__ __global__ const uint32_t& packed() const __global__ { return mData.packed; } ++ __hostdev__ __global__ uint32_t& packed() __global__ { return mData.packed; } ++ __hostdev__ __global__ const uint8_t& r() const __global__ { return mData.c[0]; } ++ __hostdev__ __global__ const uint8_t& g() const __global__ { return mData.c[1]; } ++ __hostdev__ __global__ const uint8_t& b() const __global__ { return mData.c[2]; } ++ __hostdev__ __global__ const uint8_t& a() const __global__ { return mData.c[3]; } ++ __hostdev__ __global__ uint8_t& r() __global__ { return mData.c[0]; } ++ __hostdev__ __global__ uint8_t& g() __global__ { return mData.c[1]; } ++ __hostdev__ __global__ uint8_t& b() __global__ { return mData.c[2]; } ++ __hostdev__ __global__ uint8_t& a() __global__ { return mData.c[3]; } + };// Rgba8 + + using PackedRGBA8 = Rgba8;// for backwards compatibility +@@ -660,17 +707,17 @@ + NANOVDB_ASSERT(minor < (1u << 11));// max value of minor is 2047 + NANOVDB_ASSERT(patch < (1u << 10));// max value of patch is 1023 + } +- __hostdev__ bool operator==(const Version &rhs) const {return mData == rhs.mData;} +- __hostdev__ bool operator< (const Version &rhs) const {return mData < rhs.mData;} +- __hostdev__ bool operator<=(const Version &rhs) const {return mData <= rhs.mData;} +- __hostdev__ bool operator> (const Version &rhs) const {return mData > rhs.mData;} +- __hostdev__ bool operator>=(const Version &rhs) const {return mData >= rhs.mData;} ++ __hostdev__ bool operator==(__global__ const Version &rhs) const {return mData == rhs.mData;} ++ __hostdev__ bool operator< (__global__ const Version &rhs) const {return mData < rhs.mData;} ++ __hostdev__ bool operator<=(__global__ const Version &rhs) const {return mData <= rhs.mData;} ++ __hostdev__ bool operator> (__global__ const Version &rhs) const {return mData > rhs.mData;} ++ __hostdev__ bool operator>=(__global__ const Version &rhs) const {return mData >= rhs.mData;} + __hostdev__ uint32_t id() const { return mData; } + __hostdev__ uint32_t getMajor() const { return (mData >> 21) & ((1u << 11) - 1);} + __hostdev__ uint32_t getMinor() const { return (mData >> 10) & ((1u << 11) - 1);} + __hostdev__ uint32_t getPatch() const { return mData & ((1u << 10) - 1);} + +-#ifndef __CUDACC_RTC__ ++#if !defined(__CUDACC_RTC__) && !defined(__KERNEL_METAL__) + const char* c_str() const + { + char *buffer = (char*)malloc(4 + 1 + 4 + 1 + 4 + 1);// xxxx.xxxx.xxxx\0 +@@ -749,7 +796,7 @@ + //@} + + template +-__hostdev__ inline bool isApproxZero(const Type& x) ++__hostdev__ inline bool isApproxZero(__global__ const Type& x) + { + return !(x > Tolerance::value()) && !(x < -Tolerance::value()); + } +@@ -771,10 +818,12 @@ + { + return fminf(a, b); + } ++#ifndef __KERNEL_METAL__ + __hostdev__ inline double Min(double a, double b) + { + return fmin(a, b); + } ++#endif + template + __hostdev__ inline Type Max(Type a, Type b) + { +@@ -793,45 +842,55 @@ + { + return fmaxf(a, b); + } ++#ifndef __KERNEL_METAL__ + __hostdev__ inline double Max(double a, double b) + { + return fmax(a, b); + } ++#endif + __hostdev__ inline float Clamp(float x, float a, float b) + { + return Max(Min(x, b), a); + } ++#ifndef __KERNEL_METAL__ + __hostdev__ inline double Clamp(double x, double a, double b) + { + return Max(Min(x, b), a); + } ++#endif + + __hostdev__ inline float Fract(float x) + { + return x - floorf(x); + } ++#ifndef __KERNEL_METAL__ + __hostdev__ inline double Fract(double x) + { + return x - floor(x); + } ++#endif + + __hostdev__ inline int32_t Floor(float x) + { + return int32_t(floorf(x)); + } ++#ifndef __KERNEL_METAL__ + __hostdev__ inline int32_t Floor(double x) + { + return int32_t(floor(x)); + } ++#endif + + __hostdev__ inline int32_t Ceil(float x) + { + return int32_t(ceilf(x)); + } ++#ifndef __KERNEL_METAL__ + __hostdev__ inline int32_t Ceil(double x) + { + return int32_t(ceil(x)); + } ++#endif + + template + __hostdev__ inline T Pow2(T x) +@@ -875,28 +934,54 @@ + } + + template class Vec3T> +-__hostdev__ inline CoordT Round(const Vec3T& xyz); ++__hostdev__ inline CoordT Round(__global__ const Vec3T& xyz); ++#if defined(__KERNEL_METAL__) ++template class Vec3T> ++__hostdev__ inline CoordT Round(__local__ const Vec3T& xyz); ++#endif + + template class Vec3T> +-__hostdev__ inline CoordT Round(const Vec3T& xyz) ++__hostdev__ inline CoordT Round(__global__ const Vec3T& xyz) + { + return CoordT(int32_t(rintf(xyz[0])), int32_t(rintf(xyz[1])), int32_t(rintf(xyz[2]))); + //return CoordT(int32_t(roundf(xyz[0])), int32_t(roundf(xyz[1])), int32_t(roundf(xyz[2])) ); + //return CoordT(int32_t(floorf(xyz[0] + 0.5f)), int32_t(floorf(xyz[1] + 0.5f)), int32_t(floorf(xyz[2] + 0.5f))); + } ++#if defined(__KERNEL_METAL__) ++template class Vec3T> ++__hostdev__ inline CoordT Round(__local__ const Vec3T& xyz) ++{ ++ return CoordT(int32_t(rintf(xyz[0])), int32_t(rintf(xyz[1])), int32_t(rintf(xyz[2]))); ++ //return CoordT(int32_t(roundf(xyz[0])), int32_t(roundf(xyz[1])), int32_t(roundf(xyz[2])) ); ++ //return CoordT(int32_t(floorf(xyz[0] + 0.5f)), int32_t(floorf(xyz[1] + 0.5f)), int32_t(floorf(xyz[2] + 0.5f))); ++} ++#endif + + template class Vec3T> +-__hostdev__ inline CoordT Round(const Vec3T& xyz) ++__hostdev__ inline CoordT Round(__global__ const Vec3T& xyz) + { + return CoordT(int32_t(floor(xyz[0] + 0.5)), int32_t(floor(xyz[1] + 0.5)), int32_t(floor(xyz[2] + 0.5))); + } ++#if defined(__KERNEL_METAL__) ++template class Vec3T> ++__hostdev__ inline CoordT Round(__local__ const Vec3T& xyz) ++{ ++ return CoordT(int32_t(floor(xyz[0] + 0.5)), int32_t(floor(xyz[1] + 0.5)), int32_t(floor(xyz[2] + 0.5))); ++} ++#endif + + template class Vec3T> +-__hostdev__ inline CoordT RoundDown(const Vec3T& xyz) ++__hostdev__ inline CoordT RoundDown(__global__ const Vec3T& xyz) + { + return CoordT(Floor(xyz[0]), Floor(xyz[1]), Floor(xyz[2])); + } +- ++#if defined(__KERNEL_METAL__) ++template class Vec3T> ++__hostdev__ inline CoordT RoundDown(__local__ const Vec3T& xyz) ++{ ++ return CoordT(Floor(xyz[0]), Floor(xyz[1]), Floor(xyz[2])); ++} ++#endif + //@{ + /// Return the square root of a floating-point value. + __hostdev__ inline float Sqrt(float x) +@@ -903,18 +988,24 @@ + { + return sqrtf(x); + } ++#ifndef __KERNEL_METAL__ + __hostdev__ inline double Sqrt(double x) + { + return sqrt(x); + } ++#endif + //@} + + /// Return the sign of the given value as an integer (either -1, 0 or 1). + template +-__hostdev__ inline T Sign(const T &x) { return ((T(0) < x)?T(1):T(0)) - ((x < T(0))?T(1):T(0)); } ++__hostdev__ inline T Sign(__global__ const T &x) { return ((T(0) < x)?T(1):T(0)) - ((x < T(0))?T(1):T(0)); } ++#if defined(__KERNEL_METAL__) ++template ++__hostdev__ inline T Sign(__local__ const T &x) { return ((T(0) < x)?T(1):T(0)) - ((x < T(0))?T(1):T(0)); } ++#endif + + template +-__hostdev__ inline int MinIndex(const Vec3T& v) ++__hostdev__ inline int MinIndex(__global__ const Vec3T& v) + { + #if 0 + static const int hashTable[8] = {2, 1, 9, 1, 2, 9, 0, 0}; //9 are dummy values +@@ -930,11 +1021,30 @@ + #endif + } + ++#if defined(__KERNEL_METAL__) + template +-__hostdev__ inline int MaxIndex(const Vec3T& v) ++__hostdev__ inline int MinIndex(__local__ const Vec3T& v) + { + #if 0 + static const int hashTable[8] = {2, 1, 9, 1, 2, 9, 0, 0}; //9 are dummy values ++ const int hashKey = ((v[0] < v[1]) << 2) + ((v[0] < v[2]) << 1) + (v[1] < v[2]); // ?*4+?*2+?*1 ++ return hashTable[hashKey]; ++#else ++ if (v[0] < v[1] && v[0] < v[2]) ++ return 0; ++ if (v[1] < v[2]) ++ return 1; ++ else ++ return 2; ++#endif ++} ++#endif ++ ++template ++__hostdev__ inline int MaxIndex(__global__ const Vec3T& v) ++{ ++#if 0 ++ static const int hashTable[8] = {2, 1, 9, 1, 2, 9, 0, 0}; //9 are dummy values + const int hashKey = ((v[0] > v[1]) << 2) + ((v[0] > v[2]) << 1) + (v[1] > v[2]); // ?*4+?*2+?*1 + return hashTable[hashKey]; + #else +@@ -947,6 +1057,25 @@ + #endif + } + ++#if defined(__KERNEL_METAL__) ++template ++__hostdev__ inline int MaxIndex(__local__ const Vec3T& v) ++{ ++#if 0 ++ static const int hashTable[8] = {2, 1, 9, 1, 2, 9, 0, 0}; //9 are dummy values ++ const int hashKey = ((v[0] > v[1]) << 2) + ((v[0] > v[2]) << 1) + (v[1] > v[2]); // ?*4+?*2+?*1 ++ return hashTable[hashKey]; ++#else ++ if (v[0] > v[1] && v[0] > v[2]) ++ return 0; ++ if (v[1] > v[2]) ++ return 1; ++ else ++ return 2; ++#endif ++} ++#endif ++ + /// @brief round up byteSize to the nearest wordSize, e.g. to align to machine word: AlignUp +- __hostdev__ Coord& operator=(const CoordT &other) ++ __hostdev__ __global__ Coord& operator=(__global__ const CoordT &other) __global__ + { + static_assert(sizeof(Coord) == sizeof(CoordT), "Mis-matched sizeof"); + mVec[0] = other[0]; +@@ -1025,6 +1160,17 @@ + mVec[2] = other[2]; + return *this; + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ __local__ Coord& operator=(__local__ const CoordT &other) __local__ ++ { ++ static_assert(sizeof(Coord) == sizeof(CoordT), "Mis-matched sizeof"); ++ mVec[0] = other[0]; ++ mVec[1] = other[1]; ++ mVec[2] = other[2]; ++ return *this; ++ } ++#endif + + /// @brief Return a new instance with coordinates masked by the given unsigned integer. + __hostdev__ Coord operator&(IndexType n) const { return Coord(mVec[0] & n, mVec[1] & n, mVec[2] & n); } +@@ -1036,15 +1182,15 @@ + __hostdev__ Coord operator>>(IndexType n) const { return Coord(mVec[0] >> n, mVec[1] >> n, mVec[2] >> n); } + + /// @brief Return true if this Coord is lexicographically less than the given Coord. +- __hostdev__ bool operator<(const Coord& rhs) const ++ __hostdev__ bool operator<(__global__ const Coord& rhs) const + { + return mVec[0] < rhs[0] ? true : mVec[0] > rhs[0] ? false : mVec[1] < rhs[1] ? true : mVec[1] > rhs[1] ? false : mVec[2] < rhs[2] ? true : false; + } + + // @brief Return true if the Coord components are identical. +- __hostdev__ bool operator==(const Coord& rhs) const { return mVec[0] == rhs[0] && mVec[1] == rhs[1] && mVec[2] == rhs[2]; } +- __hostdev__ bool operator!=(const Coord& rhs) const { return mVec[0] != rhs[0] || mVec[1] != rhs[1] || mVec[2] != rhs[2]; } +- __hostdev__ Coord& operator&=(int n) ++ __hostdev__ bool operator==(__global__ const Coord& rhs) const { return mVec[0] == rhs[0] && mVec[1] == rhs[1] && mVec[2] == rhs[2]; } ++ __hostdev__ bool operator!=(__global__ const Coord& rhs) const { return mVec[0] != rhs[0] || mVec[1] != rhs[1] || mVec[2] != rhs[2]; } ++ __hostdev__ __global__ Coord& operator&=(int n) __global__ + { + mVec[0] &= n; + mVec[1] &= n; +@@ -1051,7 +1197,7 @@ + mVec[2] &= n; + return *this; + } +- __hostdev__ Coord& operator<<=(uint32_t n) ++ __hostdev__ __global__ Coord& operator<<=(uint32_t n) __global__ + { + mVec[0] <<= n; + mVec[1] <<= n; +@@ -1058,7 +1204,7 @@ + mVec[2] <<= n; + return *this; + } +- __hostdev__ Coord& operator>>=(uint32_t n) ++ __hostdev__ __global__ Coord& operator>>=(uint32_t n) __global__ + { + mVec[0] >>= n; + mVec[1] >>= n; +@@ -1065,7 +1211,7 @@ + mVec[2] >>= n; + return *this; + } +- __hostdev__ Coord& operator+=(int n) ++ __hostdev__ __global__ Coord& operator+=(int n) __global__ + { + mVec[0] += n; + mVec[1] += n; +@@ -1072,9 +1218,9 @@ + mVec[2] += n; + return *this; + } +- __hostdev__ Coord operator+(const Coord& rhs) const { return Coord(mVec[0] + rhs[0], mVec[1] + rhs[1], mVec[2] + rhs[2]); } +- __hostdev__ Coord operator-(const Coord& rhs) const { return Coord(mVec[0] - rhs[0], mVec[1] - rhs[1], mVec[2] - rhs[2]); } +- __hostdev__ Coord& operator+=(const Coord& rhs) ++ __hostdev__ Coord operator+(__global__ const Coord& rhs) const { return Coord(mVec[0] + rhs[0], mVec[1] + rhs[1], mVec[2] + rhs[2]); } ++ __hostdev__ Coord operator-(__global__ const Coord& rhs) const { return Coord(mVec[0] - rhs[0], mVec[1] - rhs[1], mVec[2] - rhs[2]); } ++ __hostdev__ __global__ Coord& operator+=(__global__ const Coord& rhs) __global__ + { + mVec[0] += rhs[0]; + mVec[1] += rhs[1]; +@@ -1081,7 +1227,7 @@ + mVec[2] += rhs[2]; + return *this; + } +- __hostdev__ Coord& operator-=(const Coord& rhs) ++ __hostdev__ __global__ Coord& operator-=(__global__ const Coord& rhs) __global__ + { + mVec[0] -= rhs[0]; + mVec[1] -= rhs[1]; +@@ -1090,7 +1236,7 @@ + } + + /// @brief Perform a component-wise minimum with the other Coord. +- __hostdev__ Coord& minComponent(const Coord& other) ++ __hostdev__ __global__ Coord& minComponent(__global__ const Coord& other) __global__ + { + if (other[0] < mVec[0]) + mVec[0] = other[0]; +@@ -1102,7 +1248,7 @@ + } + + /// @brief Perform a component-wise maximum with the other Coord. +- __hostdev__ Coord& maxComponent(const Coord& other) ++ __hostdev__ __global__ Coord& maxComponent(__global__ const Coord& other) __global__ + { + if (other[0] > mVec[0]) + mVec[0] = other[0]; +@@ -1113,16 +1259,16 @@ + return *this; + } + +- __hostdev__ Coord offsetBy(ValueType dx, ValueType dy, ValueType dz) const ++ __hostdev__ Coord offsetBy(ValueType dx, ValueType dy, ValueType dz) const __global__ + { + return Coord(mVec[0] + dx, mVec[1] + dy, mVec[2] + dz); + } + +- __hostdev__ Coord offsetBy(ValueType n) const { return this->offsetBy(n, n, n); } ++ __hostdev__ Coord offsetBy(ValueType n) const __global__ { return this->offsetBy(n, n, n); } + + /// Return true if any of the components of @a a are smaller than the + /// corresponding components of @a b. +- __hostdev__ static inline bool lessThan(const Coord& a, const Coord& b) ++ __hostdev__ static inline bool lessThan(__global__ const Coord& a, __global__ const Coord& b) + { + return (a[0] < b[0] || a[1] < b[1] || a[2] < b[2]); + } +@@ -1130,7 +1276,13 @@ + /// @brief Return the largest integer coordinates that are not greater + /// than @a xyz (node centered conversion). + template +- __hostdev__ static Coord Floor(const Vec3T& xyz) { return Coord(nanovdb::Floor(xyz[0]), nanovdb::Floor(xyz[1]), nanovdb::Floor(xyz[2])); } ++ __hostdev__ static Coord Floor(__global__ const Vec3T& xyz) { return Coord(nanovdb::Floor(xyz[0]), nanovdb::Floor(xyz[1]), nanovdb::Floor(xyz[2])); } ++#if defined __KERNEL_METAL__ ++ /// @brief Return the largest integer coordinates that are not greater ++ /// than @a xyz (node centered conversion). ++ template ++ __hostdev__ static Coord Floor(__local__ const Vec3T& xyz) { return Coord(nanovdb::Floor(xyz[0]), nanovdb::Floor(xyz[1]), nanovdb::Floor(xyz[2])); } ++#endif + + /// @brief Return a hash key derived from the existing coordinates. + /// @details For details on this hash function please see the VDB paper. +@@ -1159,7 +1311,7 @@ + T mVec[3]; + + public: +- static const int SIZE = 3; ++ static __constant__ const int SIZE = 3; + using ValueType = T; + Vec3() = default; + __hostdev__ explicit Vec3(T x) +@@ -1171,18 +1323,18 @@ + { + } + template +- __hostdev__ explicit Vec3(const Vec3& v) ++ __hostdev__ explicit Vec3(__global__ const Vec3& v) + : mVec{T(v[0]), T(v[1]), T(v[2])} + { + } +- __hostdev__ explicit Vec3(const Coord& ijk) ++ __hostdev__ explicit Vec3(__global__ const Coord& ijk) + : mVec{T(ijk[0]), T(ijk[1]), T(ijk[2])} + { + } +- __hostdev__ bool operator==(const Vec3& rhs) const { return mVec[0] == rhs[0] && mVec[1] == rhs[1] && mVec[2] == rhs[2]; } +- __hostdev__ bool operator!=(const Vec3& rhs) const { return mVec[0] != rhs[0] || mVec[1] != rhs[1] || mVec[2] != rhs[2]; } ++ __hostdev__ bool operator==(__global__ const Vec3& rhs) const { return mVec[0] == rhs[0] && mVec[1] == rhs[1] && mVec[2] == rhs[2]; } ++ __hostdev__ bool operator!=(__global__ const Vec3& rhs) const { return mVec[0] != rhs[0] || mVec[1] != rhs[1] || mVec[2] != rhs[2]; } + template +- __hostdev__ Vec3& operator=(const Vec3T& rhs) ++ __hostdev__ __global__ Vec3& operator=(__global__ const Vec3T& rhs) + { + mVec[0] = rhs[0]; + mVec[1] = rhs[1]; +@@ -1189,12 +1341,18 @@ + mVec[2] = rhs[2]; + return *this; + } +- __hostdev__ const T& operator[](int i) const { return mVec[i]; } +- __hostdev__ T& operator[](int i) { return mVec[i]; } ++ __hostdev__ __global__ const T& operator[](int i) const __global__ { return mVec[i]; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __local__ const T& operator[](int i) const __local__ { return mVec[i]; } ++#endif ++ __hostdev__ __global__ T& operator[](int i) __global__ { return mVec[i]; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __local__ T& operator[](int i) __local__ { return mVec[i]; } ++#endif + template +- __hostdev__ T dot(const Vec3T& v) const { return mVec[0] * v[0] + mVec[1] * v[1] + mVec[2] * v[2]; } ++ __hostdev__ T dot(__global__ const Vec3T& v) const { return mVec[0] * v[0] + mVec[1] * v[1] + mVec[2] * v[2]; } + template +- __hostdev__ Vec3 cross(const Vec3T& v) const ++ __hostdev__ Vec3 cross(__global__ const Vec3T& v) const + { + return Vec3(mVec[1] * v[2] - mVec[2] * v[1], + mVec[2] * v[0] - mVec[0] * v[2], +@@ -1206,13 +1364,26 @@ + } + __hostdev__ T length() const { return Sqrt(this->lengthSqr()); } + __hostdev__ Vec3 operator-() const { return Vec3(-mVec[0], -mVec[1], -mVec[2]); } +- __hostdev__ Vec3 operator*(const Vec3& v) const { return Vec3(mVec[0] * v[0], mVec[1] * v[1], mVec[2] * v[2]); } +- __hostdev__ Vec3 operator/(const Vec3& v) const { return Vec3(mVec[0] / v[0], mVec[1] / v[1], mVec[2] / v[2]); } +- __hostdev__ Vec3 operator+(const Vec3& v) const { return Vec3(mVec[0] + v[0], mVec[1] + v[1], mVec[2] + v[2]); } +- __hostdev__ Vec3 operator-(const Vec3& v) const { return Vec3(mVec[0] - v[0], mVec[1] - v[1], mVec[2] - v[2]); } +- __hostdev__ Vec3 operator*(const T& s) const { return Vec3(s * mVec[0], s * mVec[1], s * mVec[2]); } +- __hostdev__ Vec3 operator/(const T& s) const { return (T(1) / s) * (*this); } +- __hostdev__ Vec3& operator+=(const Vec3& v) ++ __hostdev__ Vec3 operator*(__global__ const Vec3& v) const { return Vec3(mVec[0] * v[0], mVec[1] * v[1], mVec[2] * v[2]); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ Vec3 operator*(__local__ const Vec3& v) const { return Vec3(mVec[0] * v[0], mVec[1] * v[1], mVec[2] * v[2]); } ++#endif ++ __hostdev__ Vec3 operator/(__global__ const Vec3& v) const { return Vec3(mVec[0] / v[0], mVec[1] / v[1], mVec[2] / v[2]); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ Vec3 operator/(__local__ const Vec3& v) const { return Vec3(mVec[0] / v[0], mVec[1] / v[1], mVec[2] / v[2]); } ++#endif ++ __hostdev__ Vec3 operator+(__global__ const Vec3& v) const { return Vec3(mVec[0] + v[0], mVec[1] + v[1], mVec[2] + v[2]); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ Vec3 operator-(__local__ const Vec3& v) const { return Vec3(mVec[0] - v[0], mVec[1] - v[1], mVec[2] - v[2]); } ++ __hostdev__ Vec3 operator+(__local__ const Vec3& v) const { return Vec3(mVec[0] + v[0], mVec[1] + v[1], mVec[2] + v[2]); } ++#endif ++ __hostdev__ Vec3 operator-(__global__ const Vec3& v) const { return Vec3(mVec[0] - v[0], mVec[1] - v[1], mVec[2] - v[2]); } ++ __hostdev__ Vec3 operator*(__global__ const T& s) const { return Vec3(s * mVec[0], s * mVec[1], s * mVec[2]); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ Vec3 operator*(__local__ const T& s) const { return Vec3(s * mVec[0], s * mVec[1], s * mVec[2]); } ++#endif ++ __hostdev__ Vec3 operator/(__global__ const T& s) const { return (T(1) / s) * (*this); } ++ __hostdev__ __global__ Vec3& operator+=(__global__ const Vec3& v) + { + mVec[0] += v[0]; + mVec[1] += v[1]; +@@ -1219,7 +1390,7 @@ + mVec[2] += v[2]; + return *this; + } +- __hostdev__ Vec3& operator-=(const Vec3& v) ++ __hostdev__ __global__ Vec3& operator-=(__global__ const Vec3& v) + { + mVec[0] -= v[0]; + mVec[1] -= v[1]; +@@ -1226,7 +1397,7 @@ + mVec[2] -= v[2]; + return *this; + } +- __hostdev__ Vec3& operator*=(const T& s) ++ __hostdev__ __global__ Vec3& operator*=(__global__ const T& s) + { + mVec[0] *= s; + mVec[1] *= s; +@@ -1233,10 +1404,22 @@ + mVec[2] *= s; + return *this; + } +- __hostdev__ Vec3& operator/=(const T& s) { return (*this) *= T(1) / s; } +- __hostdev__ Vec3& normalize() { return (*this) /= this->length(); } ++#if defined __KERNEL_METAL__ ++ __hostdev__ __local__ Vec3& operator*=(__local__ const T& s) ++ { ++ mVec[0] *= s; ++ mVec[1] *= s; ++ mVec[2] *= s; ++ return *this; ++ } ++#endif ++ __hostdev__ __global__ Vec3& operator/=(__global__ const T& s) { return (*this) *= T(1) / s; } ++#if defined __KERNEL_METAL__ ++ __hostdev__ __local__ Vec3& operator/=(__local__ const T& s) { return (*this) *= T(1) / s; } ++#endif ++ __hostdev__ __global__ Vec3& normalize() { return (*this) /= this->length(); } + /// @brief Perform a component-wise minimum with the other Coord. +- __hostdev__ Vec3& minComponent(const Vec3& other) ++ __hostdev__ __global__ Vec3& minComponent(__global__ const Vec3& other) + { + if (other[0] < mVec[0]) + mVec[0] = other[0]; +@@ -1248,7 +1431,7 @@ + } + + /// @brief Perform a component-wise maximum with the other Coord. +- __hostdev__ Vec3& maxComponent(const Vec3& other) ++ __hostdev__ __global__ Vec3& maxComponent(__global__ const Vec3& other) + { + if (other[0] > mVec[0]) + mVec[0] = other[0]; +@@ -1274,15 +1457,29 @@ + }; // Vec3 + + template +-__hostdev__ inline Vec3 operator*(T1 scalar, const Vec3& vec) ++__hostdev__ inline Vec3 operator*(T1 scalar, __global__ const Vec3& vec) + { + return Vec3(scalar * vec[0], scalar * vec[1], scalar * vec[2]); + } ++#if defined(__KERNEL_METAL__) + template +-__hostdev__ inline Vec3 operator/(T1 scalar, const Vec3& vec) ++__hostdev__ inline Vec3 operator*(T1 scalar, __local__ const Vec3& vec) + { ++ return Vec3(scalar * vec[0], scalar * vec[1], scalar * vec[2]); ++} ++#endif ++template ++__hostdev__ inline Vec3 operator/(T1 scalar, __global__ const Vec3& vec) ++{ + return Vec3(scalar / vec[0], scalar / vec[1], scalar / vec[2]); + } ++#if defined(__KERNEL_METAL__) ++template ++__hostdev__ inline Vec3 operator/(T1 scalar, __local__ const Vec3& vec) ++{ ++ return Vec3(scalar / vec[0], scalar / vec[1], scalar / vec[2]); ++} ++#endif + + using Vec3R = Vec3; + using Vec3d = Vec3; +@@ -1304,7 +1501,7 @@ + T mVec[4]; + + public: +- static const int SIZE = 4; ++ static __constant__ const int SIZE = 4; + using ValueType = T; + Vec4() = default; + __hostdev__ explicit Vec4(T x) +@@ -1316,14 +1513,14 @@ + { + } + template +- __hostdev__ explicit Vec4(const Vec4& v) ++ __hostdev__ explicit Vec4(__global__ const Vec4& v) + : mVec{T(v[0]), T(v[1]), T(v[2]), T(v[3])} + { + } +- __hostdev__ bool operator==(const Vec4& rhs) const { return mVec[0] == rhs[0] && mVec[1] == rhs[1] && mVec[2] == rhs[2] && mVec[3] == rhs[3]; } +- __hostdev__ bool operator!=(const Vec4& rhs) const { return mVec[0] != rhs[0] || mVec[1] != rhs[1] || mVec[2] != rhs[2] || mVec[3] != rhs[3]; } ++ __hostdev__ bool operator==(__global__ const Vec4& rhs) const { return mVec[0] == rhs[0] && mVec[1] == rhs[1] && mVec[2] == rhs[2] && mVec[3] == rhs[3]; } ++ __hostdev__ bool operator!=(__global__ const Vec4& rhs) const { return mVec[0] != rhs[0] || mVec[1] != rhs[1] || mVec[2] != rhs[2] || mVec[3] != rhs[3]; } + template +- __hostdev__ Vec4& operator=(const Vec4T& rhs) ++ __hostdev__ __global__ Vec4& operator=(__global__ const Vec4T& rhs) + { + mVec[0] = rhs[0]; + mVec[1] = rhs[1]; +@@ -1331,10 +1528,10 @@ + mVec[3] = rhs[3]; + return *this; + } +- __hostdev__ const T& operator[](int i) const { return mVec[i]; } +- __hostdev__ T& operator[](int i) { return mVec[i]; } ++ __hostdev__ __global__ const T& operator[](int i) const { return mVec[i]; } ++ __hostdev__ __global__ T& operator[](int i) { return mVec[i]; } + template +- __hostdev__ T dot(const Vec4T& v) const { return mVec[0] * v[0] + mVec[1] * v[1] + mVec[2] * v[2] + mVec[3] * v[3]; } ++ __hostdev__ T dot(__global__ const Vec4T& v) const { return mVec[0] * v[0] + mVec[1] * v[1] + mVec[2] * v[2] + mVec[3] * v[3]; } + __hostdev__ T lengthSqr() const + { + return mVec[0] * mVec[0] + mVec[1] * mVec[1] + mVec[2] * mVec[2] + mVec[3] * mVec[3]; // 7 flops +@@ -1341,13 +1538,13 @@ + } + __hostdev__ T length() const { return Sqrt(this->lengthSqr()); } + __hostdev__ Vec4 operator-() const { return Vec4(-mVec[0], -mVec[1], -mVec[2], -mVec[3]); } +- __hostdev__ Vec4 operator*(const Vec4& v) const { return Vec4(mVec[0] * v[0], mVec[1] * v[1], mVec[2] * v[2], mVec[3] * v[3]); } +- __hostdev__ Vec4 operator/(const Vec4& v) const { return Vec4(mVec[0] / v[0], mVec[1] / v[1], mVec[2] / v[2], mVec[3] / v[3]); } +- __hostdev__ Vec4 operator+(const Vec4& v) const { return Vec4(mVec[0] + v[0], mVec[1] + v[1], mVec[2] + v[2], mVec[3] + v[3]); } +- __hostdev__ Vec4 operator-(const Vec4& v) const { return Vec4(mVec[0] - v[0], mVec[1] - v[1], mVec[2] - v[2], mVec[3] - v[3]); } +- __hostdev__ Vec4 operator*(const T& s) const { return Vec4(s * mVec[0], s * mVec[1], s * mVec[2], s * mVec[3]); } +- __hostdev__ Vec4 operator/(const T& s) const { return (T(1) / s) * (*this); } +- __hostdev__ Vec4& operator+=(const Vec4& v) ++ __hostdev__ Vec4 operator*(__global__ const Vec4& v) const { return Vec4(mVec[0] * v[0], mVec[1] * v[1], mVec[2] * v[2], mVec[3] * v[3]); } ++ __hostdev__ Vec4 operator/(__global__ const Vec4& v) const { return Vec4(mVec[0] / v[0], mVec[1] / v[1], mVec[2] / v[2], mVec[3] / v[3]); } ++ __hostdev__ Vec4 operator+(__global__ const Vec4& v) const { return Vec4(mVec[0] + v[0], mVec[1] + v[1], mVec[2] + v[2], mVec[3] + v[3]); } ++ __hostdev__ Vec4 operator-(__global__ const Vec4& v) const { return Vec4(mVec[0] - v[0], mVec[1] - v[1], mVec[2] - v[2], mVec[3] - v[3]); } ++ __hostdev__ Vec4 operator*(__global__ const T& s) const { return Vec4(s * mVec[0], s * mVec[1], s * mVec[2], s * mVec[3]); } ++ __hostdev__ Vec4 operator/(__global__ const T& s) const { return (T(1) / s) * (*this); } ++ __hostdev__ __global__ Vec4& operator+=(__global__ const Vec4& v) + { + mVec[0] += v[0]; + mVec[1] += v[1]; +@@ -1355,7 +1552,7 @@ + mVec[3] += v[3]; + return *this; + } +- __hostdev__ Vec4& operator-=(const Vec4& v) ++ __hostdev__ __global__ Vec4& operator-=(__global__ const Vec4& v) + { + mVec[0] -= v[0]; + mVec[1] -= v[1]; +@@ -1363,7 +1560,7 @@ + mVec[3] -= v[3]; + return *this; + } +- __hostdev__ Vec4& operator*=(const T& s) ++ __hostdev__ __global__ Vec4& operator*=(__global__ const T& s) + { + mVec[0] *= s; + mVec[1] *= s; +@@ -1371,10 +1568,10 @@ + mVec[3] *= s; + return *this; + } +- __hostdev__ Vec4& operator/=(const T& s) { return (*this) *= T(1) / s; } +- __hostdev__ Vec4& normalize() { return (*this) /= this->length(); } ++ __hostdev__ __global__ Vec4& operator/=(__global__ const T& s) { return (*this) *= T(1) / s; } ++ __hostdev__ __global__ Vec4& normalize() { return (*this) /= this->length(); } + /// @brief Perform a component-wise minimum with the other Coord. +- __hostdev__ Vec4& minComponent(const Vec4& other) ++ __hostdev__ __global__ Vec4& minComponent(__global__ const Vec4& other) + { + if (other[0] < mVec[0]) + mVec[0] = other[0]; +@@ -1388,7 +1585,7 @@ + } + + /// @brief Perform a component-wise maximum with the other Coord. +- __hostdev__ Vec4& maxComponent(const Vec4& other) ++ __hostdev__ __global__ Vec4& maxComponent(__global__ const Vec4& other) + { + if (other[0] > mVec[0]) + mVec[0] = other[0]; +@@ -1403,12 +1600,12 @@ + }; // Vec4 + + template +-__hostdev__ inline Vec4 operator*(T1 scalar, const Vec4& vec) ++__hostdev__ inline Vec4 operator*(T1 scalar, __global__ const Vec4& vec) + { + return Vec4(scalar * vec[0], scalar * vec[1], scalar * vec[2], scalar * vec[3]); + } + template +-__hostdev__ inline Vec4 operator/(T1 scalar, const Vec3& vec) ++__hostdev__ inline Vec4 operator/(T1 scalar, __global__ const Vec3& vec) + { + return Vec4(scalar / vec[0], scalar / vec[1], scalar / vec[2], scalar / vec[3]); + } +@@ -1428,23 +1625,23 @@ + template + struct TensorTraits + { +- static const int Rank = 0; // i.e. scalar +- static const bool IsScalar = true; +- static const bool IsVector = false; +- static const int Size = 1; ++ static __constant__ const int Rank = 0; // i.e. scalar ++ static __constant__ const bool IsScalar = true; ++ static __constant__ const bool IsVector = false; ++ static __constant__ const int Size = 1; + using ElementType = T; +- static T scalar(const T& s) { return s; } ++ static T scalar(__global__ const T& s) { return s; } + }; + + template + struct TensorTraits + { +- static const int Rank = 1; // i.e. vector +- static const bool IsScalar = false; +- static const bool IsVector = true; +- static const int Size = T::SIZE; ++ static __constant__ const int Rank = 1; // i.e. vector ++ static __constant__ const bool IsScalar = false; ++ static __constant__ const bool IsVector = true; ++ static __constant__ const int Size = T::SIZE; + using ElementType = typename T::ValueType; +- static ElementType scalar(const T& v) { return v.length(); } ++ static ElementType scalar(__global__ const T& v) { return v.length(); } + }; + + // ----------------------------> FloatTraits <-------------------------------------- +@@ -1528,71 +1725,80 @@ + // ----------------------------> matMult <-------------------------------------- + + template +-__hostdev__ inline Vec3T matMult(const float* mat, const Vec3T& xyz) ++__hostdev__ inline Vec3T matMult(__global__ const float* mat, __global__ const Vec3T& xyz) + { + return Vec3T(fmaf(xyz[0], mat[0], fmaf(xyz[1], mat[1], xyz[2] * mat[2])), + fmaf(xyz[0], mat[3], fmaf(xyz[1], mat[4], xyz[2] * mat[5])), + fmaf(xyz[0], mat[6], fmaf(xyz[1], mat[7], xyz[2] * mat[8]))); // 6 fmaf + 3 mult = 9 flops + } +- ++#if defined(__KERNEL_METAL__) + template +-__hostdev__ inline Vec3T matMult(const double* mat, const Vec3T& xyz) ++__hostdev__ inline Vec3T matMult(__global__ const float* mat, __local__ const Vec3T& xyz) + { ++ return Vec3T(fmaf(xyz[0], mat[0], fmaf(xyz[1], mat[1], xyz[2] * mat[2])), ++ fmaf(xyz[0], mat[3], fmaf(xyz[1], mat[4], xyz[2] * mat[5])), ++ fmaf(xyz[0], mat[6], fmaf(xyz[1], mat[7], xyz[2] * mat[8]))); // 6 fmaf + 3 mult = 9 flops ++} ++#endif ++#ifndef __KERNEL_METAL__ ++template ++__hostdev__ inline Vec3T matMult(__global__ const double* mat, __global__ const Vec3T& xyz) ++{ + return Vec3T(fma(static_cast(xyz[0]), mat[0], fma(static_cast(xyz[1]), mat[1], static_cast(xyz[2]) * mat[2])), + fma(static_cast(xyz[0]), mat[3], fma(static_cast(xyz[1]), mat[4], static_cast(xyz[2]) * mat[5])), + fma(static_cast(xyz[0]), mat[6], fma(static_cast(xyz[1]), mat[7], static_cast(xyz[2]) * mat[8]))); // 6 fmaf + 3 mult = 9 flops + } +- ++#endif + template +-__hostdev__ inline Vec3T matMult(const float* mat, const float* vec, const Vec3T& xyz) ++__hostdev__ inline Vec3T matMult(__global__ const float* mat, __global__ const float* vec, __global__ const Vec3T& xyz) + { + return Vec3T(fmaf(xyz[0], mat[0], fmaf(xyz[1], mat[1], fmaf(xyz[2], mat[2], vec[0]))), + fmaf(xyz[0], mat[3], fmaf(xyz[1], mat[4], fmaf(xyz[2], mat[5], vec[1]))), + fmaf(xyz[0], mat[6], fmaf(xyz[1], mat[7], fmaf(xyz[2], mat[8], vec[2])))); // 9 fmaf = 9 flops + } +- ++#ifndef __KERNEL_METAL__ + template +-__hostdev__ inline Vec3T matMult(const double* mat, const double* vec, const Vec3T& xyz) ++__hostdev__ inline Vec3T matMult(__global__ const double* mat, __global__ const double* vec, __global__ const Vec3T& xyz) + { + return Vec3T(fma(static_cast(xyz[0]), mat[0], fma(static_cast(xyz[1]), mat[1], fma(static_cast(xyz[2]), mat[2], vec[0]))), + fma(static_cast(xyz[0]), mat[3], fma(static_cast(xyz[1]), mat[4], fma(static_cast(xyz[2]), mat[5], vec[1]))), + fma(static_cast(xyz[0]), mat[6], fma(static_cast(xyz[1]), mat[7], fma(static_cast(xyz[2]), mat[8], vec[2])))); // 9 fma = 9 flops + } +- ++#endif + // matMultT: Multiply with the transpose: + + template +-__hostdev__ inline Vec3T matMultT(const float* mat, const Vec3T& xyz) ++__hostdev__ inline Vec3T matMultT(__global__ const float* mat, __global__ const Vec3T& xyz) + { + return Vec3T(fmaf(xyz[0], mat[0], fmaf(xyz[1], mat[3], xyz[2] * mat[6])), + fmaf(xyz[0], mat[1], fmaf(xyz[1], mat[4], xyz[2] * mat[7])), + fmaf(xyz[0], mat[2], fmaf(xyz[1], mat[5], xyz[2] * mat[8]))); // 6 fmaf + 3 mult = 9 flops + } +- ++#ifndef __KERNEL_METAL__ + template +-__hostdev__ inline Vec3T matMultT(const double* mat, const Vec3T& xyz) ++__hostdev__ inline Vec3T matMultT(__global__ const double* mat, __global__ const Vec3T& xyz) + { + return Vec3T(fma(static_cast(xyz[0]), mat[0], fma(static_cast(xyz[1]), mat[3], static_cast(xyz[2]) * mat[6])), + fma(static_cast(xyz[0]), mat[1], fma(static_cast(xyz[1]), mat[4], static_cast(xyz[2]) * mat[7])), + fma(static_cast(xyz[0]), mat[2], fma(static_cast(xyz[1]), mat[5], static_cast(xyz[2]) * mat[8]))); // 6 fmaf + 3 mult = 9 flops + } +- ++#endif + template +-__hostdev__ inline Vec3T matMultT(const float* mat, const float* vec, const Vec3T& xyz) ++__hostdev__ inline Vec3T matMultT(__global__ const float* mat, __global__ const float* vec, __global__ const Vec3T& xyz) + { + return Vec3T(fmaf(xyz[0], mat[0], fmaf(xyz[1], mat[3], fmaf(xyz[2], mat[6], vec[0]))), + fmaf(xyz[0], mat[1], fmaf(xyz[1], mat[4], fmaf(xyz[2], mat[7], vec[1]))), + fmaf(xyz[0], mat[2], fmaf(xyz[1], mat[5], fmaf(xyz[2], mat[8], vec[2])))); // 9 fmaf = 9 flops + } +- ++#ifndef __KERNEL_METAL__ + template +-__hostdev__ inline Vec3T matMultT(const double* mat, const double* vec, const Vec3T& xyz) ++__hostdev__ inline Vec3T matMultT(__global__ const double* mat, __global__ const double* vec, __global__ const Vec3T& xyz) + { + return Vec3T(fma(static_cast(xyz[0]), mat[0], fma(static_cast(xyz[1]), mat[3], fma(static_cast(xyz[2]), mat[6], vec[0]))), + fma(static_cast(xyz[0]), mat[1], fma(static_cast(xyz[1]), mat[4], fma(static_cast(xyz[2]), mat[7], vec[1]))), + fma(static_cast(xyz[0]), mat[2], fma(static_cast(xyz[1]), mat[5], fma(static_cast(xyz[2]), mat[8], vec[2])))); // 9 fma = 9 flops + } +- ++#endif + // ----------------------------> BBox <------------------------------------- + + // Base-class for static polymorphism (cannot be constructed directly) +@@ -1600,15 +1806,27 @@ + struct BaseBBox + { + Vec3T mCoord[2]; +- __hostdev__ bool operator==(const BaseBBox& rhs) const { return mCoord[0] == rhs.mCoord[0] && mCoord[1] == rhs.mCoord[1]; }; +- __hostdev__ bool operator!=(const BaseBBox& rhs) const { return mCoord[0] != rhs.mCoord[0] || mCoord[1] != rhs.mCoord[1]; }; +- __hostdev__ const Vec3T& operator[](int i) const { return mCoord[i]; } +- __hostdev__ Vec3T& operator[](int i) { return mCoord[i]; } +- __hostdev__ Vec3T& min() { return mCoord[0]; } +- __hostdev__ Vec3T& max() { return mCoord[1]; } +- __hostdev__ const Vec3T& min() const { return mCoord[0]; } +- __hostdev__ const Vec3T& max() const { return mCoord[1]; } +- __hostdev__ Coord& translate(const Vec3T& xyz) ++ __hostdev__ bool operator==(__global__ const BaseBBox& rhs) const __global__ { return mCoord[0] == rhs.mCoord[0] && mCoord[1] == rhs.mCoord[1]; }; ++ __hostdev__ bool operator!=(__global__ const BaseBBox& rhs) const __global__ { return mCoord[0] != rhs.mCoord[0] || mCoord[1] != rhs.mCoord[1]; }; ++ __hostdev__ __global__ const Vec3T& operator[](int i) const __global__ { return mCoord[i]; } ++ __hostdev__ __global__ Vec3T& operator[](int i) __global__ { return mCoord[i]; } ++ __hostdev__ __global__ Vec3T& min() __global__ { return mCoord[0]; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __global__ Vec3T& min() __local__ { return mCoord[0]; } ++#endif ++ __hostdev__ __global__ Vec3T& max() __global__ { return mCoord[1]; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __global__ Vec3T& max() __local__ { return mCoord[1]; } ++#endif ++ __hostdev__ __global__ const Vec3T& min() const __global__ { return mCoord[0]; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __local__ const Vec3T& min() const __local__ { return mCoord[0]; } ++#endif ++ __hostdev__ __global__ const Vec3T& max() const __global__ { return mCoord[1]; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __local__ const Vec3T& max() const __local__ { return mCoord[1]; } ++#endif ++ __hostdev__ __global__ Coord& translate(__global__ const Vec3T& xyz) __global__ + { + mCoord[0] += xyz; + mCoord[1] += xyz; +@@ -1615,7 +1833,7 @@ + return *this; + } + // @brief Expand this bounding box to enclose point (i, j, k). +- __hostdev__ BaseBBox& expand(const Vec3T& xyz) ++ __hostdev__ __global__ BaseBBox& expand(__global__ const Vec3T& xyz) __global__ + { + mCoord[0].minComponent(xyz); + mCoord[1].maxComponent(xyz); +@@ -1623,7 +1841,7 @@ + } + + /// @brief Intersect this bounding box with the given bounding box. +- __hostdev__ BaseBBox& intersect(const BaseBBox& bbox) ++ __hostdev__ __global__ BaseBBox& intersect(__global__ const BaseBBox& bbox) __global__ + { + mCoord[0].maxComponent(bbox.min()); + mCoord[1].minComponent(bbox.max()); +@@ -1634,7 +1852,7 @@ + //{ + // return BaseBBox(mCoord[0].offsetBy(-padding),mCoord[1].offsetBy(padding)); + //} +- __hostdev__ bool isInside(const Vec3T& xyz) ++ __hostdev__ bool isInside(__global__ const Vec3T& xyz) + { + if (xyz[0] < mCoord[0][0] || xyz[1] < mCoord[0][1] || xyz[2] < mCoord[0][2]) + return false; +@@ -1642,10 +1860,20 @@ + return false; + return true; + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isInside(__local__ const Vec3T& xyz) ++ { ++ if (xyz[0] < mCoord[0][0] || xyz[1] < mCoord[0][1] || xyz[2] < mCoord[0][2]) ++ return false; ++ if (xyz[0] > mCoord[1][0] || xyz[1] > mCoord[1][1] || xyz[2] > mCoord[1][2]) ++ return false; ++ return true; ++ } ++#endif + + protected: + __hostdev__ BaseBBox() {} +- __hostdev__ BaseBBox(const Vec3T& min, const Vec3T& max) ++ __hostdev__ BaseBBox(__global__ const Vec3T& min, __global__ const Vec3T& max) + : mCoord{min, max} + { + } +@@ -1659,38 +1887,45 @@ + /// @note Min is inclusive and max is exclusive. If min = max the dimension of + /// the bounding box is zero and therefore it is also empty. + template +-struct BBox : public BaseBBox ++struct BBox ++#if !defined(__KERNEL_METAL__) ++ : public BaseBBox ++#endif + { + using Vec3Type = Vec3T; + using ValueType = typename Vec3T::ValueType; + static_assert(is_floating_point::value, "Expected a floating point coordinate type"); + using BaseT = BaseBBox; ++#if defined(__KERNEL_METAL__) ++ BaseBBox mCoord; ++#else + using BaseT::mCoord; ++#endif ++ + __hostdev__ BBox() + : BaseT(Vec3T( Maximum::value()), + Vec3T(-Maximum::value())) + { + } +- __hostdev__ BBox(const Vec3T& min, const Vec3T& max) ++ __hostdev__ BBox(__global__ const Vec3T& min, __global__ const Vec3T& max) + : BaseT(min, max) + { + } +- __hostdev__ BBox(const Coord& min, const Coord& max) ++ __hostdev__ BBox(__global__ const Coord& min, __global__ const Coord& max) + : BaseT(Vec3T(ValueType(min[0]), ValueType(min[1]), ValueType(min[2])), + Vec3T(ValueType(max[0] + 1), ValueType(max[1] + 1), ValueType(max[2] + 1))) + { + } +- __hostdev__ static BBox createCube(const Coord& min, typename Coord::ValueType dim) ++ __hostdev__ static BBox createCube(__global__ const Coord& min, typename Coord::ValueType dim) + { + return BBox(min, min.offsetBy(dim)); + } +- +- __hostdev__ BBox(const BaseBBox& bbox) : BBox(bbox[0], bbox[1]) {} ++ __hostdev__ BBox(__global__ const BaseBBox& bbox) __global__ : BBox(bbox[0], bbox[1]) {} + __hostdev__ bool empty() const { return mCoord[0][0] >= mCoord[1][0] || + mCoord[0][1] >= mCoord[1][1] || + mCoord[0][2] >= mCoord[1][2]; } + __hostdev__ Vec3T dim() const { return this->empty() ? Vec3T(0) : this->max() - this->min(); } +- __hostdev__ bool isInside(const Vec3T& p) const ++ __hostdev__ bool isInside(__global__ const Vec3T& p) const + { + return p[0] > mCoord[0][0] && p[1] > mCoord[0][1] && p[2] > mCoord[0][2] && + p[0] < mCoord[1][0] && p[1] < mCoord[1][1] && p[2] < mCoord[1][2]; +@@ -1703,24 +1938,32 @@ + /// @note Both min and max are INCLUDED in the bbox so dim = max - min + 1. So, + /// if min = max the bounding box contains exactly one point and dim = 1! + template +-struct BBox : public BaseBBox ++struct BBox ++#if !defined(__KERNEL_METAL__) ++ : public BaseBBox ++#endif + { ++ + static_assert(is_same::value, "Expected \"int\" coordinate type"); + using BaseT = BaseBBox; ++#if defined(__KERNEL_METAL__) ++ BaseBBox mCoord; ++#else + using BaseT::mCoord; ++#endif + /// @brief Iterator over the domain covered by a BBox + /// @details z is the fastest-moving coordinate. + class Iterator + { +- const BBox& mBBox; ++ __global__ const BBox& mBBox; + CoordT mPos; + public: +- __hostdev__ Iterator(const BBox& b) ++ __hostdev__ Iterator(__global__ const BBox& b) + : mBBox(b) + , mPos(b.min()) + { + } +- __hostdev__ Iterator& operator++() ++ __hostdev__ __global__ Iterator& operator++() + { + if (mPos[2] < mBBox[1][2]) {// this is the most common case + ++mPos[2]; +@@ -1734,7 +1977,7 @@ + } + return *this; + } +- __hostdev__ Iterator operator++(int) ++ __hostdev__ Iterator operator++(int) __global__ + { + auto tmp = *this; + ++(*this); +@@ -1742,7 +1985,7 @@ + } + /// @brief Return @c true if the iterator still points to a valid coordinate. + __hostdev__ operator bool() const { return mPos[0] <= mBBox[1][0]; } +- __hostdev__ const CoordT& operator*() const { return mPos; } ++ __hostdev__ __global__ const CoordT& operator*() const { return mPos; } + }; // Iterator + __hostdev__ Iterator begin() const { return Iterator{*this}; } + __hostdev__ BBox() +@@ -1749,13 +1992,13 @@ + : BaseT(CoordT::max(), CoordT::min()) + { + } +- __hostdev__ BBox(const CoordT& min, const CoordT& max) ++ __hostdev__ BBox(__global__ const CoordT& min, __global__ const CoordT& max) + : BaseT(min, max) + { + } + + template +- __hostdev__ BBox(BBox& other, const SplitT&) ++ __hostdev__ BBox(__global__ BBox& other, __global__ const SplitT&) + : BaseT(other.mCoord[0], other.mCoord[1]) + { + NANOVDB_ASSERT(this->is_divisible()); +@@ -1764,7 +2007,7 @@ + other.mCoord[0][n] = mCoord[1][n] + 1; + } + +- __hostdev__ static BBox createCube(const CoordT& min, typename CoordT::ValueType dim) ++ __hostdev__ static BBox createCube(__global__ const CoordT& min, typename CoordT::ValueType dim) + { + return BBox(min, min.offsetBy(dim - 1)); + } +@@ -1778,15 +2021,23 @@ + mCoord[0][2] > mCoord[1][2]; } + __hostdev__ CoordT dim() const { return this->empty() ? Coord(0) : this->max() - this->min() + Coord(1); } + __hostdev__ uint64_t volume() const { auto d = this->dim(); return uint64_t(d[0])*uint64_t(d[1])*uint64_t(d[2]); } +- __hostdev__ bool isInside(const CoordT& p) const { return !(CoordT::lessThan(p, this->min()) || CoordT::lessThan(this->max(), p)); } +- /// @brief Return @c true if the given bounding box is inside this bounding box. +- __hostdev__ bool isInside(const BBox& b) const ++ __hostdev__ bool isInside(__global__ const CoordT& p) const { return !(CoordT::lessThan(p, this->min()) || CoordT::lessThan(this->max(), p)); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isInside(__local__ const CoordT& p) const { return !(CoordT::lessThan(p, this->min()) || CoordT::lessThan(this->max(), p)); } ++#endif ++ __hostdev__ bool isInside(__global__ const BBox& b) const + { + return !(CoordT::lessThan(b.min(), this->min()) || CoordT::lessThan(this->max(), b.max())); + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isInside(__local__ const BBox& b) const ++ { ++ return !(CoordT::lessThan(b.min(), this->min()) || CoordT::lessThan(this->max(), b.max())); ++ } ++#endif + + /// @brief Return @c true if the given bounding box overlaps with this bounding box. +- __hostdev__ bool hasOverlap(const BBox& b) const ++ __hostdev__ bool hasOverlap(__global__ const BBox& b) const + { + return !(CoordT::lessThan(this->max(), b.min()) || CoordT::lessThan(b.max(), this->min())); + } +@@ -1826,6 +2077,8 @@ + return static_cast(index); + #elif (defined(__GNUC__) || defined(__clang__)) && defined(NANOVDB_USE_INTRINSICS) + return static_cast(__builtin_ctzl(v)); ++#elif defined(__KERNEL_METAL__) ++ return ctz(v); + #else + //#warning Using software implementation for FindLowestOn(uint32_t) + static const unsigned char DeBruijn[32] = { +@@ -1856,6 +2109,8 @@ + return static_cast(index); + #elif (defined(__GNUC__) || defined(__clang__)) && defined(NANOVDB_USE_INTRINSICS) + return sizeof(unsigned long) * 8 - 1 - __builtin_clzl(v); ++#elif defined(__KERNEL_METAL__) ++ return clz(v); + #else + //#warning Using software implementation for FindHighestOn(uint32_t) + static const unsigned char DeBruijn[32] = { +@@ -1884,6 +2139,8 @@ + return static_cast(index); + #elif (defined(__GNUC__) || defined(__clang__)) && defined(NANOVDB_USE_INTRINSICS) + return static_cast(__builtin_ctzll(v)); ++#elif defined(__KERNEL_METAL__) ++ return ctz(v); + #else + //#warning Using software implementation for FindLowestOn(uint64_t) + static const unsigned char DeBruijn[64] = { +@@ -1918,6 +2175,8 @@ + return static_cast(index); + #elif (defined(__GNUC__) || defined(__clang__)) && defined(NANOVDB_USE_INTRINSICS) + return sizeof(unsigned long) * 8 - 1 - __builtin_clzll(v); ++#elif defined(__KERNEL_METAL__) ++ return clz(v); + #else + const uint32_t* p = reinterpret_cast(&v); + return p[1] ? 32u + FindHighestOn(p[1]) : FindHighestOn(p[0]); +@@ -1955,8 +2214,8 @@ + template + class Mask + { +- static constexpr uint32_t SIZE = 1U << (3 * LOG2DIM); // Number of bits in mask +- static constexpr uint32_t WORD_COUNT = SIZE >> 6; // Number of 64 bit words ++ static __constant__ constexpr uint32_t SIZE = 1U << (3 * LOG2DIM); // Number of bits in mask ++ static __constant__ constexpr uint32_t WORD_COUNT = SIZE >> 6; // Number of 64 bit words + uint64_t mWords[WORD_COUNT]; + + public: +@@ -1973,7 +2232,7 @@ + __hostdev__ uint32_t countOn() const + { + uint32_t sum = 0, n = WORD_COUNT; +- for (const uint64_t* w = mWords; n--; ++w) ++ for (__global__ const uint64_t* w = mWords; n--; ++w) + sum += CountOn(*w); + return sum; + } +@@ -1982,7 +2241,7 @@ + inline __hostdev__ uint32_t countOn(uint32_t i) const + { + uint32_t n = i >> 6, sum = CountOn( mWords[n] & ((uint64_t(1) << (i & 63u))-1u) ); +- for (const uint64_t* w = mWords; n--; ++w) sum += CountOn(*w); ++ for (__global__ const uint64_t* w = mWords; n--; ++w) sum += CountOn(*w); + return sum; + } + +@@ -1990,13 +2249,21 @@ + class Iterator + { + public: +- __hostdev__ Iterator() : mPos(Mask::SIZE), mParent(nullptr){} +- __hostdev__ Iterator(uint32_t pos, const Mask* parent) : mPos(pos), mParent(parent){} +- Iterator& operator=(const Iterator&) = default; ++ __hostdev__ Iterator() ++ : mPos(Mask::SIZE) ++ , mParent(nullptr) ++ { ++ } ++ __hostdev__ Iterator(uint32_t pos, __global__ const Mask* parent) ++ : mPos(pos) ++ , mParent(parent) ++ { ++ } ++ __global__ Iterator& operator=(__global__ const Iterator&) = default; + __hostdev__ uint32_t operator*() const { return mPos; } + __hostdev__ uint32_t pos() const { return mPos; } + __hostdev__ operator bool() const { return mPos != Mask::SIZE; } +- __hostdev__ Iterator& operator++() ++ __hostdev__ __global__ Iterator& operator++() + { + mPos = mParent->findNext(mPos + 1); + return *this; +@@ -2010,7 +2277,7 @@ + + private: + uint32_t mPos; +- const Mask* mParent; ++ __global__ const Mask* mParent; + }; // Member class Iterator + + using OnIterator = Iterator; +@@ -2034,7 +2301,7 @@ + } + + /// @brief Copy constructor +- __hostdev__ Mask(const Mask& other) ++ __hostdev__ Mask(__global__ const Mask& other) + { + for (uint32_t i = 0; i < WORD_COUNT; ++i) + mWords[i] = other.mWords[i]; +@@ -2042,29 +2309,29 @@ + + /// @brief Return a const reference to the nth word of the bit mask, for a word of arbitrary size. + template +- __hostdev__ const WordT& getWord(int n) const ++ __hostdev__ __global__ const WordT& getWord(int n) const + { + NANOVDB_ASSERT(n * 8 * sizeof(WordT) < SIZE); +- return reinterpret_cast(mWords)[n]; ++ return reinterpret_cast<__global__ const WordT*>(mWords)[n]; + } + + /// @brief Return a reference to the nth word of the bit mask, for a word of arbitrary size. + template +- __hostdev__ WordT& getWord(int n) ++ __hostdev__ __global__ WordT& getWord(int n) + { + NANOVDB_ASSERT(n * 8 * sizeof(WordT) < SIZE); +- return reinterpret_cast(mWords)[n]; ++ return reinterpret_cast<__global__ WordT*>(mWords)[n]; + } + + /// @brief Assignment operator that works with openvdb::util::NodeMask + template +- __hostdev__ Mask& operator=(const MaskT& other) ++ __hostdev__ __global__ Mask& operator=(__global__ const MaskT& other) + { + static_assert(sizeof(Mask) == sizeof(MaskT), "Mismatching sizeof"); + static_assert(WORD_COUNT == MaskT::WORD_COUNT, "Mismatching word count"); + static_assert(LOG2DIM == MaskT::LOG2DIM, "Mismatching LOG2DIM"); +- auto *src = reinterpret_cast(&other); +- uint64_t *dst = mWords; ++ __global__ auto *src = reinterpret_cast<__global__ const uint64_t*>(&other); ++ __global__ uint64_t *dst = mWords; + for (uint32_t i = 0; i < WORD_COUNT; ++i) { + *dst++ = *src++; + } +@@ -2071,7 +2338,7 @@ + return *this; + } + +- __hostdev__ bool operator==(const Mask& other) const ++ __hostdev__ bool operator==(__global__ const Mask& other) const + { + for (uint32_t i = 0; i < WORD_COUNT; ++i) { + if (mWords[i] != other.mWords[i]) return false; +@@ -2079,16 +2346,18 @@ + return true; + } + +- __hostdev__ bool operator!=(const Mask& other) const { return !((*this) == other); } ++ __hostdev__ bool operator!=(__global__ const Mask& other) const { return !((*this) == other); } + + /// @brief Return true if the given bit is set. +- __hostdev__ bool isOn(uint32_t n) const { return 0 != (mWords[n >> 6] & (uint64_t(1) << (n & 63))); } +- ++ __hostdev__ bool isOn(uint32_t n) const __global__ { return 0 != (mWords[n >> 6] & (uint64_t(1) << (n & 63))); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isOn(uint32_t n) const __local__ { return 0 != (mWords[n >> 6] & (uint64_t(1) << (n & 63))); } ++#endif + /// @brief Return true if the given bit is NOT set. +- __hostdev__ bool isOff(uint32_t n) const { return 0 == (mWords[n >> 6] & (uint64_t(1) << (n & 63))); } ++ __hostdev__ bool isOff(uint32_t n) const __global__ { return 0 == (mWords[n >> 6] & (uint64_t(1) << (n & 63))); } + + /// @brief Return true if all the bits are set in this Mask. +- __hostdev__ bool isOn() const ++ __hostdev__ bool isOn() const __global__ + { + for (uint32_t i = 0; i < WORD_COUNT; ++i) + if (mWords[i] != ~uint64_t(0)) +@@ -2095,6 +2364,15 @@ + return false; + return true; + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isOn() const __local__ ++ { ++ for (uint32_t i = 0; i < WORD_COUNT; ++i) ++ if (mWords[i] != ~uint64_t(0)) ++ return false; ++ return true; ++ } ++#endif + + /// @brief Return true if none of the bits are set in this Mask. + __hostdev__ bool isOff() const +@@ -2115,7 +2393,7 @@ + __hostdev__ void set(uint32_t n, bool On) + { + #if 1 // switch between branchless +- auto &word = mWords[n >> 6]; ++ __global__ auto &word = mWords[n >> 6]; + n &= 63; + word &= ~(uint64_t(1) << n); + word |= uint64_t(On) << n; +@@ -2149,40 +2427,40 @@ + __hostdev__ void toggle() + { + uint32_t n = WORD_COUNT; +- for (auto* w = mWords; n--; ++w) ++ for (__global__ auto* w = mWords; n--; ++w) + *w = ~*w; + } + __hostdev__ void toggle(uint32_t n) { mWords[n >> 6] ^= uint64_t(1) << (n & 63); } + + /// @brief Bitwise intersection +- __hostdev__ Mask& operator&=(const Mask& other) ++ __hostdev__ __global__ Mask& operator&=(__global__ const Mask& other) + { +- uint64_t *w1 = mWords; +- const uint64_t *w2 = other.mWords; ++ __global__ uint64_t *w1 = mWords; ++ __global__ const uint64_t *w2 = other.mWords; + for (uint32_t n = WORD_COUNT; n--; ++w1, ++w2) *w1 &= *w2; + return *this; + } + /// @brief Bitwise union +- __hostdev__ Mask& operator|=(const Mask& other) ++ __hostdev__ __global__ Mask& operator|=(__global__ const Mask& other) + { +- uint64_t *w1 = mWords; +- const uint64_t *w2 = other.mWords; ++ __global__ uint64_t *w1 = mWords; ++ __global__ const uint64_t *w2 = other.mWords; + for (uint32_t n = WORD_COUNT; n--; ++w1, ++w2) *w1 |= *w2; + return *this; + } + /// @brief Bitwise difference +- __hostdev__ Mask& operator-=(const Mask& other) ++ __hostdev__ __global__ Mask& operator-=(__global__ const Mask& other) + { +- uint64_t *w1 = mWords; +- const uint64_t *w2 = other.mWords; ++ __global__ uint64_t *w1 = mWords; ++ __global__ const uint64_t *w2 = other.mWords; + for (uint32_t n = WORD_COUNT; n--; ++w1, ++w2) *w1 &= ~*w2; + return *this; + } + /// @brief Bitwise XOR +- __hostdev__ Mask& operator^=(const Mask& other) ++ __hostdev__ __global__ Mask& operator^=(__global__ const Mask& other) + { +- uint64_t *w1 = mWords; +- const uint64_t *w2 = other.mWords; ++ __global__ uint64_t *w1 = mWords; ++ __global__ const uint64_t *w2 = other.mWords; + for (uint32_t n = WORD_COUNT; n--; ++w1, ++w2) *w1 ^= *w2; + return *this; + } +@@ -2194,7 +2472,7 @@ + __hostdev__ uint32_t findFirst() const + { + uint32_t n = 0; +- const uint64_t* w = mWords; ++ __global__ const uint64_t* w = mWords; + for (; n +- __hostdev__ void set(const Mat3T& mat, const Mat3T& invMat, const Vec3T& translate, double taper); ++ __hostdev__ void set(__global__ const Mat3T& mat, __global__ const Mat3T& invMat, __global__ const Vec3T& translate, double taper) __global__; + + /// @brief Initialize the member data + /// @note The last (4th) row of invMat is actually ignored. + template +- __hostdev__ void set(const Mat4T& mat, const Mat4T& invMat, double taper) {this->set(mat, invMat, mat[3], taper);} ++ __hostdev__ void set(__global__ const Mat4T& mat, __global__ const Mat4T& invMat, double taper) __global__ {this->set(mat, invMat, mat[3], taper);} + + template +- __hostdev__ void set(double scale, const Vec3T &translation, double taper); ++ __hostdev__ void set(double scale, __global__ const Vec3T &translation, double taper) __global__; + + template +- __hostdev__ Vec3T applyMap(const Vec3T& xyz) const { return matMult(mMatD, mVecD, xyz); } ++ __hostdev__ Vec3T applyMap(__global__ const Vec3T& xyz) const { return matMult(mMatD, mVecD, xyz); } + template +- __hostdev__ Vec3T applyMapF(const Vec3T& xyz) const { return matMult(mMatF, mVecF, xyz); } ++ __hostdev__ Vec3T applyMapF(__global__ const Vec3T& xyz) const { return matMult(mMatF, mVecF, xyz); } + + template +- __hostdev__ Vec3T applyJacobian(const Vec3T& xyz) const { return matMult(mMatD, xyz); } ++ __hostdev__ Vec3T applyJacobian(__global__ const Vec3T& xyz) const { return matMult(mMatD, xyz); } + template +- __hostdev__ Vec3T applyJacobianF(const Vec3T& xyz) const { return matMult(mMatF, xyz); } ++ __hostdev__ Vec3T applyJacobianF(__global__ const Vec3T& xyz) const { return matMult(mMatF, xyz); } + + template +- __hostdev__ Vec3T applyInverseMap(const Vec3T& xyz) const ++ __hostdev__ Vec3T applyInverseMap(__global__ const Vec3T& xyz) const __global__ + { + return matMult(mInvMatD, Vec3T(xyz[0] - mVecD[0], xyz[1] - mVecD[1], xyz[2] - mVecD[2])); + } ++#if defined(__KERNEL_METAL__) + template +- __hostdev__ Vec3T applyInverseMapF(const Vec3T& xyz) const ++ __hostdev__ Vec3T applyInverseMap(__local__ const Vec3T& xyz) const __global__ + { ++ return matMult(mInvMatD, Vec3T(xyz[0] - mVecD[0], xyz[1] - mVecD[1], xyz[2] - mVecD[2])); ++ } ++#endif ++ template ++ __hostdev__ Vec3T applyInverseMapF(const __global__ Vec3T& xyz) const __global__ ++ { + return matMult(mInvMatF, Vec3T(xyz[0] - mVecF[0], xyz[1] - mVecF[1], xyz[2] - mVecF[2])); + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ Vec3T applyInverseMapF(const __local__ Vec3T& xyz) const __global__ ++ { ++ return matMult(mInvMatF, Vec3T(xyz[0] - mVecF[0], xyz[1] - mVecF[1], xyz[2] - mVecF[2])); ++ } ++#endif + + template +- __hostdev__ Vec3T applyInverseJacobian(const Vec3T& xyz) const { return matMult(mInvMatD, xyz); } ++ __hostdev__ Vec3T applyInverseJacobian(__global__ const Vec3T& xyz) const __global__ { return matMult(mInvMatD, xyz); } + template +- __hostdev__ Vec3T applyInverseJacobianF(const Vec3T& xyz) const { return matMult(mInvMatF, xyz); } ++ __hostdev__ Vec3T applyInverseJacobianF(__global__ const Vec3T& xyz) const __global__ { return matMult(mInvMatF, xyz); } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ Vec3T applyInverseJacobianF(__local__ const Vec3T& xyz) const __global__ { return matMult(mInvMatF, xyz); } ++#endif + + template +- __hostdev__ Vec3T applyIJT(const Vec3T& xyz) const { return matMultT(mInvMatD, xyz); } ++ __hostdev__ Vec3T applyIJT(__global__ const Vec3T& xyz) const { return matMultT(mInvMatD, xyz); } + template +- __hostdev__ Vec3T applyIJTF(const Vec3T& xyz) const { return matMultT(mInvMatF, xyz); } ++ __hostdev__ Vec3T applyIJTF(__global__ const Vec3T& xyz) const { return matMultT(mInvMatF, xyz); } + }; // Map + + template +-__hostdev__ inline void Map::set(const Mat3T& mat, const Mat3T& invMat, const Vec3T& translate, double taper) ++__hostdev__ inline void Map::set(__global__ const Mat3T& mat, __global__ const Mat3T& invMat, __global__ const Vec3T& translate, double taper) __global__ + { +- float *mf = mMatF, *vf = mVecF, *mif = mInvMatF; +- double *md = mMatD, *vd = mVecD, *mid = mInvMatD; ++ __global__ float * mf = mMatF, *vf = mVecF; ++ __global__ float* mif = mInvMatF; ++ __global__ double *md = mMatD, *vd = mVecD; ++ __global__ double* mid = mInvMatD; + mTaperF = static_cast(taper); + mTaperD = taper; + for (int i = 0; i < 3; ++i) { +@@ -2295,8 +2593,19 @@ + } + + template +-__hostdev__ inline void Map::set(double dx, const Vec3T &trans, double taper) ++__hostdev__ inline void Map::set(double dx, __global__ const Vec3T &trans, double taper) __global__ + { ++#if defined __KERNEL_METAL__ ++ const float mat[3][3] = { ++ {(float)dx, 0.0, 0.0}, // row 0 ++ {0.0, (float)dx, 0.0}, // row 1 ++ {0.0, 0.0, (float)dx}, // row 2 ++ }, idx = 1.0/(float)dx, invMat[3][3] = { ++ {idx, 0.0, 0.0}, // row 0 ++ {0.0, idx, 0.0}, // row 1 ++ {0.0, 0.0, idx}, // row 2 ++ }; ++#else + const double mat[3][3] = { + {dx, 0.0, 0.0}, // row 0 + {0.0, dx, 0.0}, // row 1 +@@ -2306,6 +2615,7 @@ + {0.0, idx, 0.0}, // row 1 + {0.0, 0.0, idx}, // row 2 + }; ++#endif + this->set(mat, invMat, trans, taper); + } + +@@ -2313,7 +2623,7 @@ + + struct NANOVDB_ALIGN(NANOVDB_DATA_ALIGNMENT) GridBlindMetaData + { +- static const int MaxNameSize = 256;// due to NULL termination the maximum length is one less! ++ static __constant__ const int MaxNameSize = 256;// due to NULL termination the maximum length is one less! + int64_t mByteOffset; // byte offset to the blind data, relative to the GridData. + uint64_t mElementCount; // number of elements, e.g. point count + uint32_t mFlags; // flags +@@ -2328,10 +2638,10 @@ + return blindDataCount * sizeof(GridBlindMetaData); + } + +- __hostdev__ void setBlindData(void *ptr) { mByteOffset = PtrDiff(ptr, this); } ++ __hostdev__ void setBlindData(__global__ void *ptr) __global__ { mByteOffset = PtrDiff(ptr, this); } + + template +- __hostdev__ const T* getBlindData() const { return PtrAdd(this, mByteOffset); } ++ __hostdev__ __global__ const T* getBlindData() const { return PtrAdd(this, mByteOffset); } + + }; // GridBlindMetaData + +@@ -2430,7 +2740,7 @@ + /// @note No client code should (or can) interface with this struct so it can safely be ignored! + struct NANOVDB_ALIGN(NANOVDB_DATA_ALIGNMENT) GridData + {// sizeof(GridData) = 672B +- static const int MaxNameSize = 256;// due to NULL termination the maximum length is one less ++ static __constant__ const int MaxNameSize = 256;// due to NULL termination the maximum length is one less + uint64_t mMagic; // 8B (0) magic to validate it is valid grid data. + uint64_t mChecksum; // 8B (8). Checksum of grid buffer. + Version mVersion;// 4B (16) major, minor, and patch version numbers +@@ -2450,8 +2760,8 @@ + uint64_t mData1, mData2;// 2x8B (656) padding to 32 B alignment. mData1 is use for the total number of values indexed by an IndexGrid + + // Set and unset various bit flags +- __hostdev__ void setFlagsOff() { mFlags = uint32_t(0); } +- __hostdev__ void setMinMaxOn(bool on = true) ++ __hostdev__ void setFlagsOff() __global__ { mFlags = uint32_t(0); } ++ __hostdev__ void setMinMaxOn(bool on = true) __global__ + { + if (on) { + mFlags |= static_cast(GridFlags::HasMinMax); +@@ -2459,7 +2769,7 @@ + mFlags &= ~static_cast(GridFlags::HasMinMax); + } + } +- __hostdev__ void setBBoxOn(bool on = true) ++ __hostdev__ void setBBoxOn(bool on = true) __global__ + { + if (on) { + mFlags |= static_cast(GridFlags::HasBBox); +@@ -2467,7 +2777,7 @@ + mFlags &= ~static_cast(GridFlags::HasBBox); + } + } +- __hostdev__ void setLongGridNameOn(bool on = true) ++ __hostdev__ void setLongGridNameOn(bool on = true) __global__ + { + if (on) { + mFlags |= static_cast(GridFlags::HasLongGridName); +@@ -2475,7 +2785,7 @@ + mFlags &= ~static_cast(GridFlags::HasLongGridName); + } + } +- __hostdev__ void setAverageOn(bool on = true) ++ __hostdev__ void setAverageOn(bool on = true) __global__ + { + if (on) { + mFlags |= static_cast(GridFlags::HasAverage); +@@ -2483,7 +2793,7 @@ + mFlags &= ~static_cast(GridFlags::HasAverage); + } + } +- __hostdev__ void setStdDeviationOn(bool on = true) ++ __hostdev__ void setStdDeviationOn(bool on = true) __global__ + { + if (on) { + mFlags |= static_cast(GridFlags::HasStdDeviation); +@@ -2491,7 +2801,7 @@ + mFlags &= ~static_cast(GridFlags::HasStdDeviation); + } + } +- __hostdev__ void setBreadthFirstOn(bool on = true) ++ __hostdev__ void setBreadthFirstOn(bool on = true) __global__ + { + if (on) { + mFlags |= static_cast(GridFlags::IsBreadthFirst); +@@ -2502,37 +2812,49 @@ + + // Affine transformations based on double precision + template +- __hostdev__ Vec3T applyMap(const Vec3T& xyz) const { return mMap.applyMap(xyz); } // Pos: index -> world ++ __hostdev__ Vec3T applyMap(__global__ const Vec3T& xyz) const __global__ { return mMap.applyMap(xyz); } // Pos: index -> world + template +- __hostdev__ Vec3T applyInverseMap(const Vec3T& xyz) const { return mMap.applyInverseMap(xyz); } // Pos: world -> index ++ __hostdev__ Vec3T applyInverseMap(__global__ const Vec3T& xyz) const __global__ { return mMap.applyInverseMap(xyz); } // Pos: world -> index ++#if defined(__KERNEL_METAL__) + template +- __hostdev__ Vec3T applyJacobian(const Vec3T& xyz) const { return mMap.applyJacobian(xyz); } // Dir: index -> world ++ __hostdev__ Vec3T applyInverseMap(__local__ const Vec3T& xyz) const __global__ { return mMap.applyInverseMap(xyz); } // Pos: world -> index ++#endif + template +- __hostdev__ Vec3T applyInverseJacobian(const Vec3T& xyz) const { return mMap.applyInverseJacobian(xyz); } // Dir: world -> index ++ __hostdev__ Vec3T applyJacobian(__global__ const Vec3T& xyz) const __global__ { return mMap.applyJacobian(xyz); } // Dir: index -> world + template +- __hostdev__ Vec3T applyIJT(const Vec3T& xyz) const { return mMap.applyIJT(xyz); } ++ __hostdev__ Vec3T applyInverseJacobian(__global__ const Vec3T& xyz) const __global__ { return mMap.applyInverseJacobian(xyz); } // Dir: world -> index ++ template ++ __hostdev__ Vec3T applyIJT(__global__ const Vec3T& xyz) const __global__ { return mMap.applyIJT(xyz); } + // Affine transformations based on single precision + template +- __hostdev__ Vec3T applyMapF(const Vec3T& xyz) const { return mMap.applyMapF(xyz); } // Pos: index -> world ++ __hostdev__ Vec3T applyMapF(__global__ const Vec3T& xyz) const __global__ { return mMap.applyMapF(xyz); } // Pos: index -> world + template +- __hostdev__ Vec3T applyInverseMapF(const Vec3T& xyz) const { return mMap.applyInverseMapF(xyz); } // Pos: world -> index ++ __hostdev__ Vec3T applyInverseMapF(__global__ const Vec3T& xyz) const __global__ { return mMap.applyInverseMapF(xyz); } // Pos: world -> index ++#if defined(__KERNEL_METAL__) + template +- __hostdev__ Vec3T applyJacobianF(const Vec3T& xyz) const { return mMap.applyJacobianF(xyz); } // Dir: index -> world ++ __hostdev__ Vec3T applyInverseMapF(__local__ const Vec3T& xyz) const __global__ { return mMap.applyInverseMapF(xyz); } // Pos: world -> index ++#endif + template +- __hostdev__ Vec3T applyInverseJacobianF(const Vec3T& xyz) const { return mMap.applyInverseJacobianF(xyz); } // Dir: world -> index ++ __hostdev__ Vec3T applyJacobianF(__global__ const Vec3T& xyz) const __global__ { return mMap.applyJacobianF(xyz); } // Dir: index -> world + template +- __hostdev__ Vec3T applyIJTF(const Vec3T& xyz) const { return mMap.applyIJTF(xyz); } ++ __hostdev__ Vec3T applyInverseJacobianF(__global__ const Vec3T& xyz) const __global__ { return mMap.applyInverseJacobianF(xyz); } // Dir: world -> index ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ Vec3T applyInverseJacobianF(__local__ const Vec3T& xyz) const __global__ { return mMap.applyInverseJacobianF(xyz); } // Dir: world -> index ++#endif ++ template ++ __hostdev__ Vec3T applyIJTF(__global__ const Vec3T& xyz) const __global__ { return mMap.applyIJTF(xyz); } + + // @brief Return a non-const void pointer to the tree +- __hostdev__ void* treePtr() { return this + 1; } ++ __hostdev__ __global__ void* treePtr() __global__ { return this + 1; } + + // @brief Return a const void pointer to the tree +- __hostdev__ const void* treePtr() const { return this + 1; } ++ __hostdev__ __global__ const void* treePtr() const __global__ { return this + 1; } + + /// @brief Returns a const reference to the blindMetaData at the specified linear offset. + /// + /// @warning The linear offset is assumed to be in the valid range +- __hostdev__ const GridBlindMetaData* blindMetaData(uint32_t n) const ++ __hostdev__ __global__ const GridBlindMetaData* blindMetaData(uint32_t n) const __global__ + { + NANOVDB_ASSERT(n < mBlindMetadataCount); + return PtrAdd(this, mBlindMetadataOffset) + n; +@@ -2552,8 +2874,17 @@ + /// + /// @note This the API of this class to interface with client code + template +-class Grid : private GridData ++class Grid ++#if !defined(__KERNEL_METAL__) ++ : private GridData ++#endif + { ++#if defined(__KERNEL_METAL__) ++ GridData _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) DataType::v ++#endif + public: + using TreeType = TreeT; + using RootType = typename TreeT::RootType; +@@ -2566,183 +2897,195 @@ + /// @brief Disallow constructions, copy and assignment + /// + /// @note Only a Serializer, defined elsewhere, can instantiate this class +- Grid(const Grid&) = delete; +- Grid& operator=(const Grid&) = delete; ++ Grid(__global__ const Grid&) __global__ = delete; ++ __global__ Grid& operator=(__global__ const Grid&) __global__ = delete; + ~Grid() = delete; + +- __hostdev__ Version version() const { return DataType::mVersion; } ++ __hostdev__ Version version() const __global__ { return BASE(mVersion); } + +- __hostdev__ DataType* data() { return reinterpret_cast(this); } ++ __hostdev__ __global__ DataType* data() __global__ { return reinterpret_cast<__global__ DataType*>(this); } + +- __hostdev__ const DataType* data() const { return reinterpret_cast(this); } ++ __hostdev__ __global__ const DataType* data() const __global__ { return reinterpret_cast<__global__ const DataType*>(this); } + + /// @brief Return memory usage in bytes for this class only. + __hostdev__ static uint64_t memUsage() { return sizeof(GridData); } + + /// @brief Return the memory footprint of the entire grid, i.e. including all nodes and blind data +- __hostdev__ uint64_t gridSize() const { return DataType::mGridSize; } ++ __hostdev__ uint64_t gridSize() const __global__ { return BASE(mGridSize); } + + /// @brief Return index of this grid in the buffer +- __hostdev__ uint32_t gridIndex() const { return DataType::mGridIndex; } ++ __hostdev__ uint32_t gridIndex() const __global__ { return BASE(mGridIndex); } + + /// @brief Return total number of grids in the buffer +- __hostdev__ uint32_t gridCount() const { return DataType::mGridCount; } ++ __hostdev__ uint32_t gridCount() const __global__ { return BASE(mGridCount); } + + /// @brief @brief Return the total number of values indexed by this IndexGrid + /// + /// @note This method is only defined for IndexGrid = NanoGrid + template +- __hostdev__ typename enable_if::value, uint64_t>::type valueCount() const {return DataType::mData1;} ++ __hostdev__ typename enable_if::value, uint64_t>::type valueCount() const {return BASE(mData1);} + + /// @brief Return a const reference to the tree +- __hostdev__ const TreeT& tree() const { return *reinterpret_cast(this->treePtr()); } ++ __hostdev__ __global__ const TreeT& tree() const __global__ { return *reinterpret_cast<__global__ const TreeT*>(BASE(treePtr)()); } + + /// @brief Return a non-const reference to the tree +- __hostdev__ TreeT& tree() { return *reinterpret_cast(this->treePtr()); } ++ __hostdev__ __global__ TreeT& tree() __global__ { return *reinterpret_cast<__global__ TreeT*>(BASE(treePtr)()); } + + /// @brief Return a new instance of a ReadAccessor used to access values in this grid +- __hostdev__ AccessorType getAccessor() const { return AccessorType(this->tree().root()); } ++ __hostdev__ AccessorType getAccessor() const __global__ { return AccessorType(this->tree().root()); } + + /// @brief Return a const reference to the size of a voxel in world units +- __hostdev__ const Vec3R& voxelSize() const { return DataType::mVoxelSize; } ++ __hostdev__ const __global__ Vec3R& voxelSize() const __global__ { return BASE(mVoxelSize); } + + /// @brief Return a const reference to the Map for this grid +- __hostdev__ const Map& map() const { return DataType::mMap; } ++ __hostdev__ const __global__ Map& map() const __global__ { return BASE(mMap); } + + /// @brief world to index space transformation + template +- __hostdev__ Vec3T worldToIndex(const Vec3T& xyz) const { return this->applyInverseMap(xyz); } ++ __hostdev__ Vec3T worldToIndex(__global__ const Vec3T& xyz) const __global__ { return BASE(applyInverseMap)(xyz); } + ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ Vec3T worldToIndex(__local__ const Vec3T& xyz) const __global__ { return BASE(applyInverseMap)(xyz); } ++#endif ++ + /// @brief index to world space transformation + template +- __hostdev__ Vec3T indexToWorld(const Vec3T& xyz) const { return this->applyMap(xyz); } ++ __hostdev__ Vec3T indexToWorld(__global__ const Vec3T& xyz) const __global__ { return this->applyMap(xyz); } + + /// @brief transformation from index space direction to world space direction + /// @warning assumes dir to be normalized + template +- __hostdev__ Vec3T indexToWorldDir(const Vec3T& dir) const { return this->applyJacobian(dir); } ++ __hostdev__ Vec3T indexToWorldDir(__global__ const Vec3T& dir) const __global__ { return this->applyJacobian(dir); } + + /// @brief transformation from world space direction to index space direction + /// @warning assumes dir to be normalized + template +- __hostdev__ Vec3T worldToIndexDir(const Vec3T& dir) const { return this->applyInverseJacobian(dir); } ++ __hostdev__ Vec3T worldToIndexDir(__global__ const Vec3T& dir) const __global__ { return this->applyInverseJacobian(dir); } + + /// @brief transform the gradient from index space to world space. + /// @details Applies the inverse jacobian transform map. + template +- __hostdev__ Vec3T indexToWorldGrad(const Vec3T& grad) const { return this->applyIJT(grad); } ++ __hostdev__ Vec3T indexToWorldGrad(__global__ const Vec3T& grad) const __global__ { return this->applyIJT(grad); } + + /// @brief world to index space transformation + template +- __hostdev__ Vec3T worldToIndexF(const Vec3T& xyz) const { return this->applyInverseMapF(xyz); } ++ __hostdev__ Vec3T worldToIndexF(__global__ const Vec3T& xyz) const __global__ { return BASE(applyInverseMapF)(xyz); } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ Vec3T worldToIndexF(__local__ const Vec3T& xyz) const __global__ { return BASE(applyInverseMapF)(xyz); } ++#endif + + /// @brief index to world space transformation + template +- __hostdev__ Vec3T indexToWorldF(const Vec3T& xyz) const { return this->applyMapF(xyz); } ++ __hostdev__ Vec3T indexToWorldF(__global__ const Vec3T& xyz) const __global__ { return this->applyMapF(xyz); } + + /// @brief transformation from index space direction to world space direction + /// @warning assumes dir to be normalized + template +- __hostdev__ Vec3T indexToWorldDirF(const Vec3T& dir) const { return this->applyJacobianF(dir); } ++ __hostdev__ Vec3T indexToWorldDirF(__global__ const Vec3T& dir) const __global__ { return this->applyJacobianF(dir); } + + /// @brief transformation from world space direction to index space direction + /// @warning assumes dir to be normalized + template +- __hostdev__ Vec3T worldToIndexDirF(const Vec3T& dir) const { return this->applyInverseJacobianF(dir); } ++ __hostdev__ Vec3T worldToIndexDirF(__global__ const Vec3T& dir) const __global__ { return BASE(applyInverseJacobianF)(dir); } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ Vec3T worldToIndexDirF(__local__ const Vec3T& dir) const __global__ { return BASE(applyInverseJacobianF)(dir); } ++#endif + + /// @brief Transforms the gradient from index space to world space. + /// @details Applies the inverse jacobian transform map. + template +- __hostdev__ Vec3T indexToWorldGradF(const Vec3T& grad) const { return DataType::applyIJTF(grad); } ++ __hostdev__ Vec3T indexToWorldGradF(__global__ const Vec3T& grad) const __global__ { return BASE(applyIJTF(grad)); } + + /// @brief Computes a AABB of active values in world space +- __hostdev__ const BBox& worldBBox() const { return DataType::mWorldBBox; } ++ __hostdev__ __global__ const BBox& worldBBox() const __global__ { return BASE(mWorldBBox); } + + /// @brief Computes a AABB of active values in index space + /// + /// @note This method is returning a floating point bounding box and not a CoordBBox. This makes + /// it more useful for clipping rays. +- __hostdev__ const BBox& indexBBox() const { return this->tree().bbox(); } ++ __hostdev__ __global__ const BBox& indexBBox() const __global__ { return this->tree().bbox(); } + + /// @brief Return the total number of active voxels in this tree. +- __hostdev__ uint64_t activeVoxelCount() const { return this->tree().activeVoxelCount(); } ++ __hostdev__ uint64_t activeVoxelCount() const __global__ { return this->tree().activeVoxelCount(); } + + /// @brief Methods related to the classification of this grid +- __hostdev__ bool isValid() const { return DataType::mMagic == NANOVDB_MAGIC_NUMBER; } +- __hostdev__ const GridType& gridType() const { return DataType::mGridType; } +- __hostdev__ const GridClass& gridClass() const { return DataType::mGridClass; } +- __hostdev__ bool isLevelSet() const { return DataType::mGridClass == GridClass::LevelSet; } +- __hostdev__ bool isFogVolume() const { return DataType::mGridClass == GridClass::FogVolume; } +- __hostdev__ bool isStaggered() const { return DataType::mGridClass == GridClass::Staggered; } +- __hostdev__ bool isPointIndex() const { return DataType::mGridClass == GridClass::PointIndex; } +- __hostdev__ bool isGridIndex() const { return DataType::mGridClass == GridClass::IndexGrid; } +- __hostdev__ bool isPointData() const { return DataType::mGridClass == GridClass::PointData; } +- __hostdev__ bool isMask() const { return DataType::mGridClass == GridClass::Topology; } +- __hostdev__ bool isUnknown() const { return DataType::mGridClass == GridClass::Unknown; } +- __hostdev__ bool hasMinMax() const { return DataType::mFlags & static_cast(GridFlags::HasMinMax); } +- __hostdev__ bool hasBBox() const { return DataType::mFlags & static_cast(GridFlags::HasBBox); } +- __hostdev__ bool hasLongGridName() const { return DataType::mFlags & static_cast(GridFlags::HasLongGridName); } +- __hostdev__ bool hasAverage() const { return DataType::mFlags & static_cast(GridFlags::HasAverage); } +- __hostdev__ bool hasStdDeviation() const { return DataType::mFlags & static_cast(GridFlags::HasStdDeviation); } +- __hostdev__ bool isBreadthFirst() const { return DataType::mFlags & static_cast(GridFlags::IsBreadthFirst); } ++ __hostdev__ bool isValid() const __global__ { return BASE(mMagic) == NANOVDB_MAGIC_NUMBER; } ++ __hostdev__ const __global__ GridType& gridType() const __global__ { return BASE(mGridType); } ++ __hostdev__ const __global__ GridClass& gridClass() const __global__ { return BASE(mGridClass); } ++ __hostdev__ bool isLevelSet() const __global__ { return BASE(mGridClass) == GridClass::LevelSet; } ++ __hostdev__ bool isFogVolume() const __global__ { return BASE(mGridClass) == GridClass::FogVolume; } ++ __hostdev__ bool isStaggered() const __global__ { return BASE(mGridClass) == GridClass::Staggered; } ++ __hostdev__ bool isPointIndex() const __global__ { return BASE(mGridClass) == GridClass::PointIndex; } ++ __hostdev__ bool isGridIndex() const __global__ { return BASE(mGridClass) == GridClass::IndexGrid; } ++ __hostdev__ bool isPointData() const __global__ { return BASE(mGridClass) == GridClass::PointData; } ++ __hostdev__ bool isMask() const __global__ { return BASE(mGridClass) == GridClass::Topology; } ++ __hostdev__ bool isUnknown() const __global__ { return BASE(mGridClass) == GridClass::Unknown; } ++ __hostdev__ bool hasMinMax() const __global__ { return BASE(mFlags) & static_cast(GridFlags::HasMinMax); } ++ __hostdev__ bool hasBBox() const __global__ { return BASE(mFlags) & static_cast(GridFlags::HasBBox); } ++ __hostdev__ bool hasLongGridName() const __global__ { return BASE(mFlags) & static_cast(GridFlags::HasLongGridName); } ++ __hostdev__ bool hasAverage() const __global__ { return BASE(mFlags) & static_cast(GridFlags::HasAverage); } ++ __hostdev__ bool hasStdDeviation() const __global__ { return BASE(mFlags) & static_cast(GridFlags::HasStdDeviation); } ++ __hostdev__ bool isBreadthFirst() const __global__ { return BASE(mFlags) & static_cast(GridFlags::IsBreadthFirst); } + + /// @brief return true if the specified node type is layed out breadth-first in memory and has a fixed size. + /// This allows for sequential access to the nodes. + template +- __hostdev__ bool isSequential() const { return NodeT::FIXED_SIZE && this->isBreadthFirst(); } ++ __hostdev__ bool isSequential() const __global__ { return NodeT::FIXED_SIZE && this->isBreadthFirst(); } + + /// @brief return true if the specified node level is layed out breadth-first in memory and has a fixed size. + /// This allows for sequential access to the nodes. + template +- __hostdev__ bool isSequential() const { return NodeTrait::type::FIXED_SIZE && this->isBreadthFirst(); } ++ __hostdev__ bool isSequential() const __global__ { return NodeTrait::type::FIXED_SIZE && this->isBreadthFirst(); } + + /// @brief Return a c-string with the name of this grid +- __hostdev__ const char* gridName() const ++ __hostdev__ __global__ const char* gridName() const __global__ + { + if (this->hasLongGridName()) { + NANOVDB_ASSERT(DataType::mBlindMetadataCount>0); +- const auto &metaData = this->blindMetaData(DataType::mBlindMetadataCount-1);// always the last ++ __global__ const auto &metaData = this->blindMetaData(BASE(mBlindMetadataCount)-1);// always the last + NANOVDB_ASSERT(metaData.mDataClass == GridBlindDataClass::GridName); + return metaData.template getBlindData(); + } +- return DataType::mGridName; ++ return BASE(mGridName); + } + + /// @brief Return a c-string with the name of this grid, truncated to 255 characters +- __hostdev__ const char* shortGridName() const { return DataType::mGridName; } +- ++ __hostdev__ __global__ const char* shortGridName() const __global__ { return BASE(mGridName); } + /// @brief Return checksum of the grid buffer. +- __hostdev__ uint64_t checksum() const { return DataType::mChecksum; } ++ __hostdev__ uint64_t checksum() const __global__ { return BASE(mChecksum); } + + /// @brief Return true if this grid is empty, i.e. contains no values or nodes. +- __hostdev__ bool isEmpty() const { return this->tree().isEmpty(); } ++ __hostdev__ bool isEmpty() const __global__ { return this->tree().isEmpty(); } + + /// @brief Return the count of blind-data encoded in this grid +- __hostdev__ uint32_t blindDataCount() const { return DataType::mBlindMetadataCount; } ++ __hostdev__ uint32_t blindDataCount() const __global__ { return BASE(mBlindMetadataCount); } + + /// @brief Return the index of the blind data with specified semantic if found, otherwise -1. +- __hostdev__ int findBlindDataForSemantic(GridBlindDataSemantic semantic) const; ++ __hostdev__ int findBlindDataForSemantic(GridBlindDataSemantic semantic) const __global__; + + /// @brief Returns a const pointer to the blindData at the specified linear offset. + /// + /// @warning Point might be NULL and the linear offset is assumed to be in the valid range +- __hostdev__ const void* blindData(uint32_t n) const ++ __hostdev__ __global__ const void* blindData(uint32_t n) const __global__ + { +- if (DataType::mBlindMetadataCount == 0u) { ++ if (BASE(mBlindMetadataCount) == 0u) { + return nullptr; + } + NANOVDB_ASSERT(n < DataType::mBlindMetadataCount); + return this->blindMetaData(n).template getBlindData(); + } ++ ++ __hostdev__ __global__ const GridBlindMetaData& blindMetaData(uint32_t n) const __global__ { return *BASE(blindMetaData)(n); } + +- __hostdev__ const GridBlindMetaData& blindMetaData(uint32_t n) const { return *DataType::blindMetaData(n); } +- + private: + static_assert(sizeof(GridData) % NANOVDB_DATA_ALIGNMENT == 0, "sizeof(GridData) is misaligned"); + }; // Class Grid + + template +-__hostdev__ int Grid::findBlindDataForSemantic(GridBlindDataSemantic semantic) const ++__hostdev__ int Grid::findBlindDataForSemantic(GridBlindDataSemantic semantic) const __global__ + { + for (uint32_t i = 0, n = this->blindDataCount(); i < n; ++i) + if (this->blindMetaData(i).mSemantic == semantic) +@@ -2762,14 +3105,14 @@ + uint64_t mVoxelCount;// 8B, total number of active voxels in the root and all its child nodes. + // No padding since it's always 32B aligned + template +- __hostdev__ void setRoot(const RootT* root) { mNodeOffset[3] = PtrDiff(root, this); } ++ __hostdev__ void setRoot(__global__ const RootT* root) __global__ { mNodeOffset[3] = PtrDiff(root, this); } + template +- __hostdev__ RootT* getRoot() { return PtrAdd(this, mNodeOffset[3]); } ++ __hostdev__ __global__ RootT* getRoot() __global__ { return PtrAdd(this, mNodeOffset[3]); } + template +- __hostdev__ const RootT* getRoot() const { return PtrAdd(this, mNodeOffset[3]); } ++ __hostdev__ __global__ const RootT* getRoot() const __global__ { return PtrAdd(this, mNodeOffset[3]); } + + template +- __hostdev__ void setFirstNode(const NodeT* node) ++ __hostdev__ void setFirstNode(__global__ const NodeT* node) __global__ + { + mNodeOffset[NodeT::LEVEL] = node ? PtrDiff(node, this) : 0; + } +@@ -2795,8 +3138,17 @@ + + /// @brief VDB Tree, which is a thin wrapper around a RootNode. + template +-class Tree : private TreeData ++class Tree ++#if !defined(__KERNEL_METAL__) ++ : private TreeData ++#endif + { ++#if defined(__KERNEL_METAL__) ++ TreeData _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) DataType::v ++#endif + static_assert(RootT::LEVEL == 3, "Tree depth is not supported"); + static_assert(RootT::ChildNodeType::LOG2DIM == 5, "Tree configuration is not supported"); + static_assert(RootT::ChildNodeType::ChildNodeType::LOG2DIM == 4, "Tree configuration is not supported"); +@@ -2817,47 +3169,54 @@ + using Node0 = LeafNodeType; + + /// @brief This class cannot be constructed or deleted +- Tree() = delete; +- Tree(const Tree&) = delete; +- Tree& operator=(const Tree&) = delete; +- ~Tree() = delete; ++ Tree() __global__ = delete; ++ Tree(__global__ const Tree&) __global__ = delete; ++ __global__ Tree& operator=(__global__ const Tree&) __global__ = delete; ++ ~Tree() __global__ = delete; + +- __hostdev__ DataType* data() { return reinterpret_cast(this); } ++ __hostdev__ __global__ DataType* data() __global__ { return reinterpret_cast<__global__ DataType*>(this); } + +- __hostdev__ const DataType* data() const { return reinterpret_cast(this); } ++ __hostdev__ __global__ const DataType* data() const __global__ { return reinterpret_cast<__global__ const DataType*>(this); } + + /// @brief return memory usage in bytes for the class + __hostdev__ static uint64_t memUsage() { return sizeof(DataType); } + +- __hostdev__ RootT& root() { return *DataType::template getRoot(); } ++ __hostdev__ __global__ RootT& root() __global__ { return *BASE(template) getRoot(); } + +- __hostdev__ const RootT& root() const { return *DataType::template getRoot(); } ++ __hostdev__ __global__ const RootT& root() const __global__ { return *BASE(template) getRoot(); } + +- __hostdev__ AccessorType getAccessor() const { return AccessorType(this->root()); } ++ __hostdev__ AccessorType getAccessor() const __global__ { return AccessorType(this->root()); } + + /// @brief Return the value of the given voxel (regardless of state or location in the tree.) +- __hostdev__ ValueType getValue(const CoordType& ijk) const { return this->root().getValue(ijk); } ++ __hostdev__ ValueType getValue(__global__ const CoordType& ijk) const __global__ { return this->root().getValue(ijk); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ ValueType getValue(__local__ const CoordType& ijk) const __global__ { return this->root().getValue(ijk); } ++#endif + + /// @brief Return the active state of the given voxel (regardless of state or location in the tree.) +- __hostdev__ bool isActive(const CoordType& ijk) const { return this->root().isActive(ijk); } ++ __hostdev__ bool isActive(__global__ const CoordType& ijk) const __global__ { return this->root().isActive(ijk); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isActive(__local__ const CoordType& ijk) const __global__ { return this->root().isActive(ijk); } ++ __hostdev__ bool isActive(__local__ const CoordType& ijk) const __local__ { return this->root().isActive(ijk); } ++#endif + + /// @brief Return true if this tree is empty, i.e. contains no values or nodes +- __hostdev__ bool isEmpty() const { return this->root().isEmpty(); } ++ __hostdev__ bool isEmpty() const __global__ { return this->root().isEmpty(); } + + /// @brief Combines the previous two methods in a single call +- __hostdev__ bool probeValue(const CoordType& ijk, ValueType& v) const { return this->root().probeValue(ijk, v); } ++ __hostdev__ bool probeValue(__global__ const CoordType& ijk, __global__ ValueType& v) const { return this->root().probeValue(ijk, v); } + + /// @brief Return a const reference to the background value. +- __hostdev__ const ValueType& background() const { return this->root().background(); } ++ __hostdev__ __global__ const ValueType& background() const __global__ { return this->root().background(); } + + /// @brief Sets the extrema values of all the active values in this tree, i.e. in all nodes of the tree +- __hostdev__ void extrema(ValueType& min, ValueType& max) const; ++ __hostdev__ void extrema(__global__ ValueType& min, __global__ ValueType& max) const __global__; + + /// @brief Return a const reference to the index bounding box of all the active values in this tree, i.e. in all nodes of the tree +- __hostdev__ const BBox& bbox() const { return this->root().bbox(); } ++ __hostdev__ __global__ const BBox& bbox() const __global__ { return this->root().bbox(); } + + /// @brief Return the total number of active voxels in this tree. +- __hostdev__ uint64_t activeVoxelCount() const { return DataType::mVoxelCount; } ++ __hostdev__ uint64_t activeVoxelCount() const __global__ { return BASE(mVoxelCount); } + + /// @brief Return the total number of active tiles at the specified level of the tree. + /// +@@ -2864,23 +3223,23 @@ + /// @details level = 1,2,3 corresponds to active tile count in lower internal nodes, upper + /// internal nodes, and the root level. Note active values at the leaf level are + /// referred to as active voxels (see activeVoxelCount defined above). +- __hostdev__ const uint32_t& activeTileCount(uint32_t level) const ++ __hostdev__ __global__ const uint32_t& activeTileCount(uint32_t level) const __global__ + { + NANOVDB_ASSERT(level > 0 && level <= 3);// 1, 2, or 3 +- return DataType::mTileCount[level - 1]; ++ return BASE(mTileCount)[level - 1]; + } + + template +- __hostdev__ uint32_t nodeCount() const ++ __hostdev__ uint32_t nodeCount() const __global__ + { + static_assert(NodeT::LEVEL < 3, "Invalid NodeT"); +- return DataType::mNodeCount[NodeT::LEVEL]; ++ return BASE(mNodeCount)[NodeT::LEVEL]; + } + +- __hostdev__ uint32_t nodeCount(int level) const ++ __hostdev__ uint32_t nodeCount(int level) const __global__ + { + NANOVDB_ASSERT(level < 3); +- return DataType::mNodeCount[level]; ++ return BASE(mNodeCount)[level]; + } + + /// @brief return a pointer to the first node of the specified type +@@ -2887,9 +3246,9 @@ + /// + /// @warning Note it may return NULL if no nodes exist + template +- __hostdev__ NodeT* getFirstNode() ++ __hostdev__ __global__ NodeT* getFirstNode() __global__ + { +- const uint64_t offset = DataType::mNodeOffset[NodeT::LEVEL]; ++ const uint64_t offset = BASE(mNodeOffset)[NodeT::LEVEL]; + return offset>0 ? PtrAdd(this, offset) : nullptr; + } + +@@ -2897,9 +3256,9 @@ + /// + /// @warning Note it may return NULL if no nodes exist + template +- __hostdev__ const NodeT* getFirstNode() const ++ __hostdev__ __global__ const NodeT* getFirstNode() const __global__ + { +- const uint64_t offset = DataType::mNodeOffset[NodeT::LEVEL]; ++ const uint64_t offset = BASE(mNodeOffset)[NodeT::LEVEL]; + return offset>0 ? PtrAdd(this, offset) : nullptr; + } + +@@ -2907,8 +3266,8 @@ + /// + /// @warning Note it may return NULL if no nodes exist + template +- __hostdev__ typename NodeTrait::type* +- getFirstNode() ++ __hostdev__ __global__ typename NodeTrait::type* ++ getFirstNode() __global__ + { + return this->template getFirstNode::type>(); + } +@@ -2917,27 +3276,28 @@ + /// + /// @warning Note it may return NULL if no nodes exist + template +- __hostdev__ const typename NodeTrait::type* +- getFirstNode() const ++ __hostdev__ __global__ const typename NodeTrait::type* ++ getFirstNode() const __global__ + { + return this->template getFirstNode::type>(); + } + + /// @brief Template specializations of getFirstNode +- __hostdev__ LeafNodeType* getFirstLeaf() {return this->getFirstNode();} +- __hostdev__ const LeafNodeType* getFirstLeaf() const {return this->getFirstNode();} +- __hostdev__ typename NodeTrait::type* getFirstLower() {return this->getFirstNode<1>();} +- __hostdev__ const typename NodeTrait::type* getFirstLower() const {return this->getFirstNode<1>();} +- __hostdev__ typename NodeTrait::type* getFirstUpper() {return this->getFirstNode<2>();} +- __hostdev__ const typename NodeTrait::type* getFirstUpper() const {return this->getFirstNode<2>();} ++ __hostdev__ __global__ LeafNodeType* getFirstLeaf() {return this->getFirstNode();} ++ __hostdev__ __global__ const LeafNodeType* getFirstLeaf() const {return this->getFirstNode();} ++ __hostdev__ __global__ typename NodeTrait::type* getFirstLower() {return this->getFirstNode<1>();} ++ __hostdev__ __global__ const typename NodeTrait::type* getFirstLower() const {return this->getFirstNode<1>();} ++ __hostdev__ __global__ typename NodeTrait::type* getFirstUpper() {return this->getFirstNode<2>();} ++ __hostdev__ __global__ const typename NodeTrait::type* getFirstUpper() const {return this->getFirstNode<2>();} + + private: + static_assert(sizeof(DataType) % NANOVDB_DATA_ALIGNMENT == 0, "sizeof(TreeData) is misaligned"); + ++#undef BASE + }; // Tree class + + template +-__hostdev__ void Tree::extrema(ValueType& min, ValueType& max) const ++__hostdev__ void Tree::extrema(__global__ ValueType& min, __global__ ValueType& max) const __global__ + { + min = this->root().minimum(); + max = this->root().maximum(); +@@ -2955,13 +3315,13 @@ + using BuildT = typename ChildT::BuildType;// in rare cases BuildType != ValueType, e.g. then BuildType = ValueMask and ValueType = bool + using CoordT = typename ChildT::CoordType; + using StatsT = typename ChildT::FloatType; +- static constexpr bool FIXED_SIZE = false; ++ static __constant__ constexpr bool FIXED_SIZE = false; + + /// @brief Return a key based on the coordinates of a voxel + #ifdef USE_SINGLE_ROOT_KEY + using KeyT = uint64_t; + template +- __hostdev__ static KeyT CoordToKey(const CoordType& ijk) ++ __hostdev__ static KeyT CoordToKey(__global__ const CoordType& ijk) + { + static_assert(sizeof(CoordT) == sizeof(CoordType), "Mismatching sizeof"); + static_assert(32 - ChildT::TOTAL <= 21, "Cannot use 64 bit root keys"); +@@ -2969,9 +3329,20 @@ + (KeyT(uint32_t(ijk[1]) >> ChildT::TOTAL) << 21) | // y is the middle 21 bits + (KeyT(uint32_t(ijk[0]) >> ChildT::TOTAL) << 42); // x is the upper 21 bits + } +- __hostdev__ static CoordT KeyToCoord(const KeyT& key) ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ static KeyT CoordToKey(__local__ const CoordType& ijk) + { +- static constexpr uint64_t MASK = (1u << 21) - 1; ++ static_assert(sizeof(CoordT) == sizeof(CoordType), "Mismatching sizeof"); ++ static_assert(32 - ChildT::TOTAL <= 21, "Cannot use 64 bit root keys"); ++ return (KeyT(uint32_t(ijk[2]) >> ChildT::TOTAL)) | // z is the lower 21 bits ++ (KeyT(uint32_t(ijk[1]) >> ChildT::TOTAL) << 21) | // y is the middle 21 bits ++ (KeyT(uint32_t(ijk[0]) >> ChildT::TOTAL) << 42); // x is the upper 21 bits ++ } ++#endif ++ static __constant__ constexpr uint64_t MASK = (1u << 21) - 1; ++ __hostdev__ static CoordT KeyToCoord(__global__ const KeyT& key) ++ { + return CoordT(((key >> 42) & MASK) << ChildT::TOTAL, + ((key >> 21) & MASK) << ChildT::TOTAL, + (key & MASK) << ChildT::TOTAL); +@@ -2978,8 +3349,8 @@ + } + #else + using KeyT = CoordT; +- __hostdev__ static KeyT CoordToKey(const CoordT& ijk) { return ijk & ~ChildT::MASK; } +- __hostdev__ static CoordT KeyToCoord(const KeyT& key) { return key; } ++ __hostdev__ static KeyT CoordToKey(__global__ const CoordT& ijk) { return ijk & ~ChildT::MASK; } ++ __hostdev__ static CoordT KeyToCoord(__global__ const KeyT& key) { return key; } + #endif + BBox mBBox; // 24B. AABB of active values in index space. + uint32_t mTableSize; // 4B. number of tiles and child pointers in the root node +@@ -3000,13 +3371,13 @@ + struct NANOVDB_ALIGN(NANOVDB_DATA_ALIGNMENT) Tile + { + template +- __hostdev__ void setChild(const CoordType& k, const ChildT *ptr, const RootData *data) ++ __hostdev__ void setChild(__global__ const CoordType& k, __global__ const ChildT *ptr, __global__ const RootData *data) + { + key = CoordToKey(k); + child = PtrDiff(ptr, data); + } + template +- __hostdev__ void setValue(const CoordType& k, bool s, const ValueType &v) ++ __hostdev__ void setValue(__global__ const CoordType& k, bool s, __global__ const ValueType &v) + { + key = CoordToKey(k); + state = s; +@@ -3013,10 +3384,10 @@ + value = v; + child = 0; + } +- __hostdev__ bool isChild() const { return child!=0; } +- __hostdev__ bool isValue() const { return child==0; } +- __hostdev__ bool isActive() const { return child==0 && state; } +- __hostdev__ CoordT origin() const { return KeyToCoord(key); } ++ __hostdev__ bool isChild() const __global__ { return child!=0; } ++ __hostdev__ bool isValue() const __global__ { return child==0; } ++ __hostdev__ bool isActive() const __global__ { return child==0 && state; } ++ __hostdev__ CoordT origin() const __global__ { return KeyToCoord(key); } + KeyT key; // USE_SINGLE_ROOT_KEY ? 8B : 12B + int64_t child; // 8B. signed byte offset from this node to the child node. 0 means it is a constant tile, so use value. + uint32_t state; // 4B. state of tile value +@@ -3026,53 +3397,64 @@ + /// @brief Returns a non-const reference to the tile at the specified linear offset. + /// + /// @warning The linear offset is assumed to be in the valid range +- __hostdev__ const Tile* tile(uint32_t n) const ++ __hostdev__ __global__ const Tile* tile(uint32_t n) const + { + NANOVDB_ASSERT(n < mTableSize); +- return reinterpret_cast(this + 1) + n; ++ return reinterpret_cast<__global__ const Tile*>(this + 1) + n; + } +- __hostdev__ Tile* tile(uint32_t n) ++ __hostdev__ __global__ Tile* tile(uint32_t n) + { + NANOVDB_ASSERT(n < mTableSize); +- return reinterpret_cast(this + 1) + n; ++ return reinterpret_cast<__global__ Tile*>(this + 1) + n; + } + + /// @brief Returns a const reference to the child node in the specified tile. + /// + /// @warning A child node is assumed to exist in the specified tile +- __hostdev__ ChildT* getChild(const Tile* tile) ++ __hostdev__ __global__ ChildT* getChild(__global__ const Tile* tile) __global__ + { + NANOVDB_ASSERT(tile->child); + return PtrAdd(this, tile->child); + } +- __hostdev__ const ChildT* getChild(const Tile* tile) const ++ __hostdev__ __global__ const ChildT* getChild(__global__ const Tile* tile) const __global__ + { + NANOVDB_ASSERT(tile->child); + return PtrAdd(this, tile->child); + } + +- __hostdev__ const ValueT& getMin() const { return mMinimum; } +- __hostdev__ const ValueT& getMax() const { return mMaximum; } +- __hostdev__ const StatsT& average() const { return mAverage; } +- __hostdev__ const StatsT& stdDeviation() const { return mStdDevi; } ++ __hostdev__ __global__ const ValueT& getMin() const { return mMinimum; } ++ __hostdev__ __global__ const ValueT& getMax() const { return mMaximum; } ++ __hostdev__ __global__ const StatsT& average() const { return mAverage; } ++ __hostdev__ __global__ const StatsT& stdDeviation() const { return mStdDevi; } + +- __hostdev__ void setMin(const ValueT& v) { mMinimum = v; } +- __hostdev__ void setMax(const ValueT& v) { mMaximum = v; } +- __hostdev__ void setAvg(const StatsT& v) { mAverage = v; } +- __hostdev__ void setDev(const StatsT& v) { mStdDevi = v; } ++ __hostdev__ void setMin(__global__ const ValueT& v) { mMinimum = v; } ++ __hostdev__ void setMax(__global__ const ValueT& v) { mMaximum = v; } ++ __hostdev__ void setAvg(__global__ const StatsT& v) { mAverage = v; } ++ __hostdev__ void setDev(__global__ const StatsT& v) { mStdDevi = v; } + + /// @brief This class cannot be constructed or deleted + RootData() = delete; +- RootData(const RootData&) = delete; +- RootData& operator=(const RootData&) = delete; ++ RootData(__global__ const RootData&) = delete; ++ __global__ RootData& operator=(__global__ const RootData&) = delete; + ~RootData() = delete; + }; // RootData + + /// @brief Top-most node of the VDB tree structure. + template +-class RootNode : private RootData ++class RootNode ++#if !defined(__KERNEL_METAL__) ++ : private RootData ++#endif + { + public: ++#if defined(__KERNEL_METAL__) ++ ++ RootData _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) DataType::v ++#endif ++ + using DataType = RootData; + using LeafNodeType = typename ChildT::LeafNodeType; + using ChildNodeType = ChildT; +@@ -3086,27 +3468,27 @@ + using BBoxType = BBox; + using AccessorType = DefaultReadAccessor; + using Tile = typename DataType::Tile; +- static constexpr bool FIXED_SIZE = DataType::FIXED_SIZE; ++ static __constant__ constexpr bool FIXED_SIZE = DataType::FIXED_SIZE; + +- static constexpr uint32_t LEVEL = 1 + ChildT::LEVEL; // level 0 = leaf ++ static __constant__ constexpr uint32_t LEVEL = 1 + ChildT::LEVEL; // level 0 = leaf + + class ChildIterator + { +- const DataType *mParent; +- uint32_t mPos, mSize; ++ __global__ const DataType *mParent; ++ uint32_t mPos, mSize; + public: + __hostdev__ ChildIterator() : mParent(nullptr), mPos(0), mSize(0) {} +- __hostdev__ ChildIterator(const RootNode *parent) : mParent(parent->data()), mPos(0), mSize(parent->tileCount()) { ++ __hostdev__ ChildIterator(__global__ const RootNode *parent) : mParent(parent->data()), mPos(0), mSize(parent->tileCount()) { + NANOVDB_ASSERT(mParent); + while (mPostile(mPos)->isChild()) ++mPos; + } +- ChildIterator& operator=(const ChildIterator&) = default; +- __hostdev__ const ChildT& operator*() const {NANOVDB_ASSERT(*this); return *mParent->getChild(mParent->tile(mPos));} +- __hostdev__ const ChildT* operator->() const {NANOVDB_ASSERT(*this); return mParent->getChild(mParent->tile(mPos));} ++ __global__ ChildIterator& operator=(__global__ const ChildIterator&) = default; ++ __hostdev__ __global__ const ChildT& operator*() const {NANOVDB_ASSERT(*this); return *mParent->getChild(mParent->tile(mPos));} ++ __hostdev__ __global__ const ChildT* operator->() const {NANOVDB_ASSERT(*this); return mParent->getChild(mParent->tile(mPos));} + __hostdev__ CoordType getOrigin() const { NANOVDB_ASSERT(*this); mParent->tile(mPos)->origin();} + __hostdev__ operator bool() const {return mPos < mSize;} + __hostdev__ uint32_t pos() const {return mPos;} +- __hostdev__ ChildIterator& operator++() { ++ __hostdev__ __global__ ChildIterator& operator++() { + NANOVDB_ASSERT(mParent); + ++mPos; + while (mPos < mSize && mParent->tile(mPos)->isValue()) ++mPos; +@@ -3123,21 +3505,21 @@ + + class ValueIterator + { +- const DataType *mParent; +- uint32_t mPos, mSize; ++ __global__ const DataType *mParent; ++ uint32_t mPos, mSize; + public: + __hostdev__ ValueIterator() : mParent(nullptr), mPos(0), mSize(0) {} +- __hostdev__ ValueIterator(const RootNode *parent) : mParent(parent->data()), mPos(0), mSize(parent->tileCount()){ ++ __hostdev__ ValueIterator(__global__ const RootNode *parent) : mParent(parent->data()), mPos(0), mSize(parent->tileCount()){ + NANOVDB_ASSERT(mParent); + while (mPos < mSize && mParent->tile(mPos)->isChild()) ++mPos; + } +- ValueIterator& operator=(const ValueIterator&) = default; ++ __global__ ValueIterator& operator=(__global__ const ValueIterator&) = default; + __hostdev__ ValueType operator*() const {NANOVDB_ASSERT(*this); return mParent->tile(mPos)->value;} + __hostdev__ bool isActive() const {NANOVDB_ASSERT(*this); return mParent->tile(mPos)->state;} + __hostdev__ operator bool() const {return mPos < mSize;} + __hostdev__ uint32_t pos() const {return mPos;} + __hostdev__ CoordType getOrigin() const { NANOVDB_ASSERT(*this); mParent->tile(mPos)->origin();} +- __hostdev__ ValueIterator& operator++() { ++ __hostdev__ __global__ ValueIterator& operator++() { + NANOVDB_ASSERT(mParent); + ++mPos; + while (mPos < mSize && mParent->tile(mPos)->isChild()) ++mPos; +@@ -3154,20 +3536,20 @@ + + class ValueOnIterator + { +- const DataType *mParent; ++ __global__ const DataType *mParent; + uint32_t mPos, mSize; + public: + __hostdev__ ValueOnIterator() : mParent(nullptr), mPos(0), mSize(0) {} +- __hostdev__ ValueOnIterator(const RootNode *parent) : mParent(parent->data()), mPos(0), mSize(parent->tileCount()){ ++ __hostdev__ ValueOnIterator(__global__ const RootNode *parent) : mParent(parent->data()), mPos(0), mSize(parent->tileCount()){ + NANOVDB_ASSERT(mParent); + while (mPos < mSize && !mParent->tile(mPos)->isActive()) ++mPos; + } +- ValueOnIterator& operator=(const ValueOnIterator&) = default; ++ __global__ ValueOnIterator& operator=(__global__ const ValueOnIterator&) = default; + __hostdev__ ValueType operator*() const {NANOVDB_ASSERT(*this); return mParent->tile(mPos)->value;} + __hostdev__ operator bool() const {return mPos < mSize;} + __hostdev__ uint32_t pos() const {return mPos;} + __hostdev__ CoordType getOrigin() const { NANOVDB_ASSERT(*this); mParent->tile(mPos)->origin();} +- __hostdev__ ValueOnIterator& operator++() { ++ __hostdev__ __global__ ValueOnIterator& operator++() { + NANOVDB_ASSERT(mParent); + ++mPos; + while (mPos < mSize && !mParent->tile(mPos)->isActive()) ++mPos; +@@ -3183,75 +3565,107 @@ + ValueOnIterator beginValueOn() const {return ValueOnIterator(this);} + + /// @brief This class cannot be constructed or deleted +- RootNode() = delete; +- RootNode(const RootNode&) = delete; +- RootNode& operator=(const RootNode&) = delete; +- ~RootNode() = delete; ++ RootNode() __global__ = delete; ++ RootNode(__global__ const RootNode&) __global__ = delete; ++ __global__ RootNode& operator=(__global__ const RootNode&) __global__ = delete; ++ ~RootNode() __global__ = delete; + +- __hostdev__ AccessorType getAccessor() const { return AccessorType(*this); } ++ __hostdev__ AccessorType getAccessor() const __global__ { return AccessorType(*this); } + +- __hostdev__ DataType* data() { return reinterpret_cast(this); } ++ __hostdev__ __global__ DataType* data() __global__ { return reinterpret_cast<__global__ DataType*>(this); } + +- __hostdev__ const DataType* data() const { return reinterpret_cast(this); } ++ __hostdev__ __global__ const DataType* data() const __global__ { return reinterpret_cast<__global__ const DataType*>(this); } + + /// @brief Return a const reference to the index bounding box of all the active values in this tree, i.e. in all nodes of the tree +- __hostdev__ const BBoxType& bbox() const { return DataType::mBBox; } ++ __hostdev__ __global__ const BBoxType& bbox() const __global__ { return BASE(mBBox); } + + /// @brief Return the total number of active voxels in the root and all its child nodes. + + /// @brief Return a const reference to the background value, i.e. the value associated with + /// any coordinate location that has not been set explicitly. +- __hostdev__ const ValueType& background() const { return DataType::mBackground; } ++ __hostdev__ __global__ const ValueType& background() const __global__ { return DataType::mBackground; } + + /// @brief Return the number of tiles encoded in this root node +- __hostdev__ const uint32_t& tileCount() const { return DataType::mTableSize; } ++ __hostdev__ __global__ const uint32_t& tileCount() const __global__ { return DataType::mTableSize; } + + /// @brief Return a const reference to the minimum active value encoded in this root node and any of its child nodes +- __hostdev__ const ValueType& minimum() const { return this->getMin(); } ++ __hostdev__ __global__ const ValueType& minimum() const __global__ { return this->getMin(); } + + /// @brief Return a const reference to the maximum active value encoded in this root node and any of its child nodes +- __hostdev__ const ValueType& maximum() const { return this->getMax(); } ++ __hostdev__ __global__ const ValueType& maximum() const __global__ { return this->getMax(); } + + /// @brief Return a const reference to the average of all the active values encoded in this root node and any of its child nodes +- __hostdev__ const FloatType& average() const { return DataType::mAverage; } ++ __hostdev__ __global__ const FloatType& average() const __global__ { return DataType::mAverage; } + + /// @brief Return the variance of all the active values encoded in this root node and any of its child nodes +- __hostdev__ FloatType variance() const { return DataType::mStdDevi * DataType::mStdDevi; } ++ __hostdev__ FloatType variance() const __global__ { return DataType::mStdDevi * DataType::mStdDevi; } + + /// @brief Return a const reference to the standard deviation of all the active values encoded in this root node and any of its child nodes +- __hostdev__ const FloatType& stdDeviation() const { return DataType::mStdDevi; } ++ __hostdev__ __global__ const FloatType& stdDeviation() const __global__ { return DataType::mStdDevi; } + + /// @brief Return the expected memory footprint in bytes with the specified number of tiles + __hostdev__ static uint64_t memUsage(uint32_t tableSize) { return sizeof(RootNode) + tableSize * sizeof(Tile); } + + /// @brief Return the actual memory footprint of this root node +- __hostdev__ uint64_t memUsage() const { return sizeof(RootNode) + DataType::mTableSize * sizeof(Tile); } ++ __hostdev__ uint64_t memUsage() const __global__ { return sizeof(RootNode) + DataType::mTableSize * sizeof(Tile); } + + /// @brief Return the value of the given voxel +- __hostdev__ ValueType getValue(const CoordType& ijk) const ++ __hostdev__ ValueType getValue(__global__ const CoordType& ijk) const __global__ + { +- if (const Tile* tile = this->probeTile(ijk)) { ++ if (__global__ const Tile* tile = this->probeTile(ijk)) { + return tile->isChild() ? this->getChild(tile)->getValue(ijk) : tile->value; + } + return DataType::mBackground; + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ ValueType getValue(__local__ const CoordType& ijk) const __global__ ++ { ++ if (__global__ const Tile* tile = this->findTile(ijk)) { ++ return tile->isChild() ? this->getChild(tile)->getValue(ijk) : tile->value; ++ } ++ return DataType::mBackground; ++ } ++ __hostdev__ ValueType getValue(__local__ const CoordType& ijk) const __local__ ++ { ++ if (__global__ const Tile* tile = this->findTile(ijk)) { ++ return tile->isChild() ? this->getChild(tile)->getValue(ijk) : tile->value; ++ } ++ return DataType::mBackground; ++ } ++#endif + +- __hostdev__ bool isActive(const CoordType& ijk) const ++ __hostdev__ bool isActive(__global__ const CoordType& ijk) const __global__ + { +- if (const Tile* tile = this->probeTile(ijk)) { +- return tile->isChild() ? this->getChild(tile)->isActive(ijk) : tile->state; ++ if (__global__ const Tile* tile = this->findTile(ijk)) { ++ return tile->isChild() ? BASE(getChild)(tile)->isActive(ijk) : tile->state; + } + return false; + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isActive(__local__ const CoordType& ijk) const __global__ ++ { ++ if (__global__ const Tile* tile = this->findTile(ijk)) { ++ return tile->isChild() ? BASE(getChild)(tile)->isActive(ijk) : tile->state; ++ } ++ return false; ++ } ++ __hostdev__ bool isActive(__local__ const CoordType& ijk) const __local__ ++ { ++ if (__global__ const Tile* tile = this->findTile(ijk)) { ++ return tile->isChild() ? BASE(getChild)(tile)->isActive(ijk) : tile->state; ++ } ++ return false; ++ } ++#endif + + /// @brief Return true if this RootNode is empty, i.e. contains no values or nodes +- __hostdev__ bool isEmpty() const { return DataType::mTableSize == uint32_t(0); } ++ __hostdev__ bool isEmpty() const __global__ { return BASE(mTableSize) == uint32_t(0); } + +- __hostdev__ bool probeValue(const CoordType& ijk, ValueType& v) const ++ __hostdev__ bool probeValue(__global__ const CoordType& ijk, __global__ ValueType& v) const __global__ + { +- if (const Tile* tile = this->probeTile(ijk)) { ++ if (__global__ const Tile* tile = this->probeTile(ijk)) { + if (tile->isChild()) { +- const auto *child = this->getChild(tile); ++ __global__ const auto *child = this->getChild(tile); + return child->probeValue(ijk, v); + } + v = tile->value; +@@ -3260,20 +3674,35 @@ + v = DataType::mBackground; + return false; + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool probeValue(__local__ const CoordType& ijk, __local__ ValueType& v) const __global__ ++ { ++ if (__global__ const Tile* tile = this->findTile(ijk)) { ++ if (tile->isChild()) { ++ __global__ const auto *child = BASE(getChild)(tile); ++ return child->probeValue(ijk, v); ++ } ++ v = tile->value; ++ return tile->state; ++ } ++ v = BASE(mBackground); ++ return false; ++ } ++#endif + +- __hostdev__ const LeafNodeType* probeLeaf(const CoordType& ijk) const ++ __hostdev__ __global__ const LeafNodeType* probeLeaf(__global__ const CoordType& ijk) const + { +- const Tile* tile = this->probeTile(ijk); ++ __global__ const Tile* tile = this->probeTile(ijk); + if (tile && tile->isChild()) { +- const auto *child = this->getChild(tile); ++ const __global__ auto *child = this->getChild(tile); + return child->probeLeaf(ijk); + } + return nullptr; + } + +- __hostdev__ const ChildNodeType* probeChild(const CoordType& ijk) const ++ __hostdev__ __global__ const ChildNodeType* probeChild(__global__ const CoordType& ijk) const + { +- const Tile* tile = this->probeTile(ijk); ++ __global__ const Tile* tile = this->probeTile(ijk); + if (tile && tile->isChild()) { + return this->getChild(tile); + } +@@ -3280,13 +3709,14 @@ + return nullptr; + } + ++ + /// @brief Find and return a Tile of this root node +- __hostdev__ const Tile* probeTile(const CoordType& ijk) const ++ __hostdev__ __global__ const Tile* probeTile(__global__ const CoordType& ijk) const __global__ + { +- const Tile* tiles = reinterpret_cast(this + 1); +- const auto key = DataType::CoordToKey(ijk); ++ __global__ const Tile* tiles = reinterpret_cast<__global__ const Tile*>(this + 1); ++ const auto key = BASE(CoordToKey)(ijk); + #if 1 // switch between linear and binary seach +- for (uint32_t i = 0; i < DataType::mTableSize; ++i) { ++ for (uint32_t i = 0; i < BASE(mTableSize); ++i) { + if (tiles[i].key == key) return &tiles[i]; + } + #else// do not enable binary search if tiles are not guaranteed to be sorted!!!!!! +@@ -3306,6 +3736,33 @@ + #endif + return nullptr; + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __global__ const Tile* findTile(__local__ const CoordType& ijk) const __global__ ++ { ++ __global__ const Tile* tiles = reinterpret_cast<__global__ const Tile*>(this + 1); ++ const auto key = BASE(CoordToKey)(ijk); ++#if 1 // switch between linear and binary seach ++ for (uint32_t i = 0; i < BASE(mTableSize); ++i) { ++ if (tiles[i].key == key) return &tiles[i]; ++ } ++#else// do not enable binary search if tiles are not guaranteed to be sorted!!!!!! ++ // binary-search of pre-sorted elements ++ int32_t low = 0, high = DataType::mTableSize; // low is inclusive and high is exclusive ++ while (low != high) { ++ int mid = low + ((high - low) >> 1); ++ const Tile* tile = &tiles[mid]; ++ if (tile->key == key) { ++ return tile; ++ } else if (tile->key < key) { ++ low = mid + 1; ++ } else { ++ high = mid; ++ } ++ } ++#endif ++ return nullptr; ++ } ++#endif + + private: + static_assert(sizeof(DataType) % NANOVDB_DATA_ALIGNMENT == 0, "sizeof(RootData) is misaligned"); +@@ -3319,12 +3776,12 @@ + + /// @brief Private method to return node information and update a ReadAccessor + template +- __hostdev__ typename AccT::NodeInfo getNodeInfoAndCache(const CoordType& ijk, const AccT& acc) const ++ __hostdev__ typename AccT::NodeInfo getNodeInfoAndCache(__global__ const CoordType& ijk, __global__ const AccT& acc) const + { + using NodeInfoT = typename AccT::NodeInfo; +- if (const Tile* tile = this->probeTile(ijk)) { ++ if (__global__ const Tile* tile = this->probeTile(ijk)) { + if (tile->isChild()) { +- const auto *child = this->getChild(tile); ++ __global__ const auto *child = this->getChild(tile); + acc.insert(ijk, child); + return child->getNodeInfoAndCache(ijk, acc); + } +@@ -3337,11 +3794,11 @@ + + /// @brief Private method to return a voxel value and update a ReadAccessor + template +- __hostdev__ ValueType getValueAndCache(const CoordType& ijk, const AccT& acc) const ++ __hostdev__ ValueType getValueAndCache(__global__ const CoordType& ijk, __global__ const AccT& acc) const __global__ + { +- if (const Tile* tile = this->probeTile(ijk)) { ++ if (__global__ const Tile* tile = this->probeTile(ijk)) { + if (tile->isChild()) { +- const auto *child = this->getChild(tile); ++ __global__ const auto *child = this->getChild(tile); + acc.insert(ijk, child); + return child->getValueAndCache(ijk, acc); + } +@@ -3349,25 +3806,66 @@ + } + return DataType::mBackground; + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ ValueType getValueAndCache(__local__ const CoordType& ijk, __local__ const AccT& acc) const __global__ ++ { ++ if (__global__ const Tile* tile = this->findTile(ijk)) { ++ if (tile->isChild()) { ++ __global__ const auto *child = BASE(getChild)(tile); ++ acc.insert(ijk, child); ++ return child->getValueAndCache(ijk, acc); ++ } ++ return tile->value; ++ } ++ return BASE(mBackground); ++ } ++ template ++ __hostdev__ ValueType getValueAndCache(__local__ const CoordType& ijk, __local__ const AccT& acc) const __local__ ++ { ++ if (__global__ const Tile* tile = this->findTile(ijk)) { ++ if (tile->isChild()) { ++ __global__ const auto *child = BASE(getChild)(tile); ++ acc.insert(ijk, child); ++ return child->getValueAndCache(ijk, acc); ++ } ++ return tile->value; ++ } ++ return BASE(mBackground); ++ } ++#endif + + template +- __hostdev__ bool isActiveAndCache(const CoordType& ijk, const AccT& acc) const ++ __hostdev__ bool isActiveAndCache(__global__ const CoordType& ijk, __global__ const AccT& acc) const + { +- const Tile* tile = this->probeTile(ijk); ++ __global__ const Tile* tile = this->probeTile(ijk); + if (tile && tile->isChild()) { +- const auto *child = this->getChild(tile); ++ __global__ const auto *child = BASE(getChild)(tile); + acc.insert(ijk, child); + return child->isActiveAndCache(ijk, acc); + } + return false; + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ bool isActiveAndCache(__local__ const CoordType& ijk, __local__ const AccT& acc) const __global__ ++ { ++ __global__ const Tile* tile = this->findTile(ijk); ++ if (tile && tile->isChild()) { ++ __global__ const auto *child = BASE(getChild)(tile); ++ acc.insert(ijk, child); ++ return child->isActiveAndCache(ijk, acc); ++ } ++ return false; ++ } ++#endif + + template +- __hostdev__ bool probeValueAndCache(const CoordType& ijk, ValueType& v, const AccT& acc) const ++ __hostdev__ bool probeValueAndCache(__global__ const CoordType& ijk, __global__ ValueType& v, __global__ const AccT& acc) const + { +- if (const Tile* tile = this->probeTile(ijk)) { ++ if (__global__ const Tile* tile = this->probeTile(ijk)) { + if (tile->isChild()) { +- const auto *child = this->getChild(tile); ++ __global__ const auto *child = BASE(getChild)(tile); + acc.insert(ijk, child); + return child->probeValueAndCache(ijk, v, acc); + } +@@ -3379,11 +3877,11 @@ + } + + template +- __hostdev__ const LeafNodeType* probeLeafAndCache(const CoordType& ijk, const AccT& acc) const ++ __hostdev__ __global__ const LeafNodeType* probeLeafAndCache(__global__ const CoordType& ijk, __global__ const AccT& acc) const + { +- const Tile* tile = this->probeTile(ijk); ++ __global__ const Tile* tile = this->probeTile(ijk); + if (tile && tile->isChild()) { +- const auto *child = this->getChild(tile); ++ __global__ const auto *child = BASE(getChild)(tile); + acc.insert(ijk, child); + return child->probeLeafAndCache(ijk, acc); + } +@@ -3391,11 +3889,11 @@ + } + + template +- __hostdev__ uint32_t getDimAndCache(const CoordType& ijk, const RayT& ray, const AccT& acc) const ++ __hostdev__ uint32_t getDimAndCache(__global__ const CoordType& ijk, __global__ const RayT& ray, __global__ const AccT& acc) const __global__ + { +- if (const Tile* tile = this->probeTile(ijk)) { ++ if (__global__ const Tile* tile = this->probeTile(ijk)) { + if (tile->isChild()) { +- const auto *child = this->getChild(tile); ++ __global__ const auto *child = BASE(getChild)(tile); + acc.insert(ijk, child); + return child->getDimAndCache(ijk, ray, acc); + } +@@ -3403,7 +3901,23 @@ + } + return ChildNodeType::dim(); // background + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ uint32_t getDimAndCache(__local__ const CoordType& ijk, __local__ const RayT& ray, __local__ const AccT& acc) const __global__ ++ { ++ if (__global__ const Tile* tile = this->probeTile(ijk)) { ++ if (tile->isChild()) { ++ __global__ const auto *child = BASE(getChild)(tile); ++ acc.insert(ijk, child); ++ return child->getDimAndCache(ijk, ray, acc); ++ } ++ return 1 << ChildT::TOTAL; //tile value ++ } ++ return ChildNodeType::dim(); // background ++ } ++#endif + ++#undef BASE + }; // RootNode class + + // After the RootNode the memory layout is assumed to be the sorted Tiles +@@ -3421,7 +3935,7 @@ + using StatsT = typename ChildT::FloatType; + using CoordT = typename ChildT::CoordType; + using MaskT = typename ChildT::template MaskType; +- static constexpr bool FIXED_SIZE = true; ++ static __constant__ constexpr bool FIXED_SIZE = true; + + union Tile + { +@@ -3429,8 +3943,8 @@ + int64_t child;//signed 64 bit byte offset relative to the InternalData!! + /// @brief This class cannot be constructed or deleted + Tile() = delete; +- Tile(const Tile&) = delete; +- Tile& operator=(const Tile&) = delete; ++ Tile(__global__ const Tile&) = delete; ++ __global__ Tile& operator=(__global__ const Tile&) = delete; + ~Tile() = delete; + }; + +@@ -3456,7 +3970,7 @@ + + __hostdev__ static uint64_t memUsage() { return sizeof(InternalData); } + +- __hostdev__ void setChild(uint32_t n, const void *ptr) ++ __hostdev__ void setChild(uint32_t n, __global__ const void *ptr) + { + NANOVDB_ASSERT(mChildMask.isOn(n)); + mTable[n].child = PtrDiff(ptr, this); +@@ -3463,7 +3977,7 @@ + } + + template +- __hostdev__ void setValue(uint32_t n, const ValueT &v) ++ __hostdev__ void setValue(uint32_t n, __global__ const ValueT &v) + { + NANOVDB_ASSERT(!mChildMask.isOn(n)); + mTable[n].value = v; +@@ -3470,18 +3984,18 @@ + } + + /// @brief Returns a pointer to the child node at the specifed linear offset. +- __hostdev__ ChildT* getChild(uint32_t n) ++ __hostdev__ __global__ ChildT* getChild(uint32_t n) __global__ + { + NANOVDB_ASSERT(mChildMask.isOn(n)); + return PtrAdd(this, mTable[n].child); + } +- __hostdev__ const ChildT* getChild(uint32_t n) const ++ __hostdev__ __global__ const ChildT* getChild(uint32_t n) const __global__ + { + NANOVDB_ASSERT(mChildMask.isOn(n)); + return PtrAdd(this, mTable[n].child); + } + +- __hostdev__ ValueT getValue(uint32_t n) const ++ __hostdev__ ValueT getValue(uint32_t n) const __global__ + { + NANOVDB_ASSERT(!mChildMask.isOn(n)); + return mTable[n].value; +@@ -3496,29 +4010,38 @@ + __hostdev__ bool isChild(uint32_t n) const {return mChildMask.isOn(n);} + + template +- __hostdev__ void setOrigin(const T& ijk) { mBBox[0] = ijk; } ++ __hostdev__ void setOrigin(__global__ const T& ijk) { mBBox[0] = ijk; } + +- __hostdev__ const ValueT& getMin() const { return mMinimum; } +- __hostdev__ const ValueT& getMax() const { return mMaximum; } +- __hostdev__ const StatsT& average() const { return mAverage; } +- __hostdev__ const StatsT& stdDeviation() const { return mStdDevi; } ++ __hostdev__ __global__ const ValueT& getMin() const { return mMinimum; } ++ __hostdev__ __global__ const ValueT& getMax() const { return mMaximum; } ++ __hostdev__ __global__ const StatsT& average() const { return mAverage; } ++ __hostdev__ __global__ const StatsT& stdDeviation() const { return mStdDevi; } + +- __hostdev__ void setMin(const ValueT& v) { mMinimum = v; } +- __hostdev__ void setMax(const ValueT& v) { mMaximum = v; } +- __hostdev__ void setAvg(const StatsT& v) { mAverage = v; } +- __hostdev__ void setDev(const StatsT& v) { mStdDevi = v; } ++ __hostdev__ void setMin(__global__ const ValueT& v) { mMinimum = v; } ++ __hostdev__ void setMax(__global__ const ValueT& v) { mMaximum = v; } ++ __hostdev__ void setAvg(__global__ const StatsT& v) { mAverage = v; } ++ __hostdev__ void setDev(__global__ const StatsT& v) { mStdDevi = v; } + + /// @brief This class cannot be constructed or deleted + InternalData() = delete; +- InternalData(const InternalData&) = delete; +- InternalData& operator=(const InternalData&) = delete; ++ InternalData(__global__ const InternalData&) = delete; ++ __global__ InternalData& operator=(__global__ const InternalData&) = delete; + ~InternalData() = delete; + }; // InternalData + + /// @brief Internal nodes of a VDB treedim(), + template +-class InternalNode : private InternalData ++class InternalNode ++#if !defined(__KERNEL_METAL__) ++ : private InternalData ++#endif + { ++#if defined(__KERNEL_METAL__) ++ InternalData _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) DataType::v ++#endif + public: + using DataType = InternalData; + using ValueType = typename DataType::ValueT; +@@ -3527,31 +4050,40 @@ + using LeafNodeType = typename ChildT::LeafNodeType; + using ChildNodeType = ChildT; + using CoordType = typename ChildT::CoordType; +- static constexpr bool FIXED_SIZE = DataType::FIXED_SIZE; ++ static __constant__ constexpr bool FIXED_SIZE = DataType::FIXED_SIZE; + template + using MaskType = typename ChildT::template MaskType; + template + using MaskIterT = typename Mask::template Iterator; + +- static constexpr uint32_t LOG2DIM = Log2Dim; +- static constexpr uint32_t TOTAL = LOG2DIM + ChildT::TOTAL; // dimension in index space +- static constexpr uint32_t DIM = 1u << TOTAL; // number of voxels along each axis of this node +- static constexpr uint32_t SIZE = 1u << (3 * LOG2DIM); // number of tile values (or child pointers) +- static constexpr uint32_t MASK = (1u << TOTAL) - 1u; +- static constexpr uint32_t LEVEL = 1 + ChildT::LEVEL; // level 0 = leaf +- static constexpr uint64_t NUM_VALUES = uint64_t(1) << (3 * TOTAL); // total voxel count represented by this node ++ static __constant__ constexpr uint32_t LOG2DIM = Log2Dim; ++ static __constant__ constexpr uint32_t TOTAL = LOG2DIM + ChildT::TOTAL; // dimension in index space ++ static __constant__ constexpr uint32_t DIM = 1u << TOTAL; // number of voxels along each axis of this node ++ static __constant__ constexpr uint32_t SIZE = 1u << (3 * LOG2DIM); // number of tile values (or child pointers) ++ static __constant__ constexpr uint32_t MASK = (1u << TOTAL) - 1u; ++ static __constant__ constexpr uint32_t LEVEL = 1 + ChildT::LEVEL; // level 0 = leaf ++ static __constant__ constexpr uint64_t NUM_VALUES = uint64_t(1) << (3 * TOTAL); // total voxel count represented by this node + + /// @brief Visits child nodes of this node only +- class ChildIterator : public MaskIterT ++ class ChildIterator ++#if !defined (__KERNEL_METAL__) ++ : public MaskIterT ++#endif + { ++#if defined (__KERNEL_METAL__) ++ MaskIterT BaseT; ++#define BASE(v) BaseT.v ++#else + using BaseT = MaskIterT; +- const DataType *mParent; ++#define BASE(v) BaseT::v ++#endif ++ __global__ const DataType *mParent; + public: + __hostdev__ ChildIterator() : BaseT(), mParent(nullptr) {} +- __hostdev__ ChildIterator(const InternalNode* parent) : BaseT(parent->data()->mChildMask.beginOn()), mParent(parent->data()) {} +- ChildIterator& operator=(const ChildIterator&) = default; +- __hostdev__ const ChildT& operator*() const {NANOVDB_ASSERT(*this); return *mParent->getChild(BaseT::pos());} +- __hostdev__ const ChildT* operator->() const {NANOVDB_ASSERT(*this); return mParent->getChild(BaseT::pos());} ++ __hostdev__ ChildIterator(__global__ const InternalNode* parent) : BaseT(parent->data()->mChildMask.beginOn()), mParent(parent->data()) {} ++ __global__ ChildIterator& operator=(__global__ const ChildIterator&) = default; ++ __hostdev__ __global__ const ChildT& operator*() const {NANOVDB_ASSERT(*this); return *mParent->getChild(BASE(pos)());} ++ __hostdev__ __global__ const ChildT* operator->() const {NANOVDB_ASSERT(*this); return mParent->getChild(BASE(pos)());} + __hostdev__ CoordType getOrigin() const { NANOVDB_ASSERT(*this); return (*this)->origin();} + }; // Member class ChildIterator + +@@ -3558,45 +4090,69 @@ + ChildIterator beginChild() const {return ChildIterator(this);} + + /// @brief Visits all tile values in this node, i.e. both inactive and active tiles +- class ValueIterator : public MaskIterT ++ class ValueIterator ++#if !defined (__KERNEL_METAL__) ++ : public MaskIterT ++#endif + { ++#if defined (__KERNEL_METAL__) ++ MaskIterT BaseT; ++#define BASE(v) BaseT.v ++#else + using BaseT = MaskIterT; +- const InternalNode *mParent; ++#define BASE(v) BaseT::v ++#endif ++ __global__ const InternalNode *mParent; + public: + __hostdev__ ValueIterator() : BaseT(), mParent(nullptr) {} +- __hostdev__ ValueIterator(const InternalNode* parent) : BaseT(parent->data()->mChildMask.beginOff()), mParent(parent) {} +- ValueIterator& operator=(const ValueIterator&) = default; +- __hostdev__ ValueType operator*() const {NANOVDB_ASSERT(*this); return mParent->data()->getValue(BaseT::pos());} +- __hostdev__ CoordType getOrigin() const { NANOVDB_ASSERT(*this); return mParent->localToGlobalCoord(BaseT::pos());} +- __hostdev__ bool isActive() const { NANOVDB_ASSERT(*this); return mParent->data()->isActive(BaseT::mPos);} ++ __hostdev__ ValueIterator(__global__ const InternalNode* parent) : BaseT(parent->data()->mChildMask.beginOff()), mParent(parent) {} ++ __global__ ValueIterator& operator=(__global__ const ValueIterator&) = default; ++ __hostdev__ ValueType operator*() const {NANOVDB_ASSERT(*this); return mParent->data()->getValue(BASE(pos)());} ++ __hostdev__ CoordType getOrigin() const { NANOVDB_ASSERT(*this); return mParent->localToGlobalCoord(BASE(pos)());} ++ __hostdev__ bool isActive() const { NANOVDB_ASSERT(*this); return mParent->data()->isActive(BASE(mPos));} + }; // Member class ValueIterator + + ValueIterator beginValue() const {return ValueIterator(this);} + + /// @brief Visits active tile values of this node only +- class ValueOnIterator : public MaskIterT ++ class ValueOnIterator ++#if !defined (__KERNEL_METAL__) ++ : public MaskIterT ++#endif + { ++#if defined (__KERNEL_METAL__) ++ MaskIterT BaseT; ++#define BASE(v) BaseT.v ++#else + using BaseT = MaskIterT; +- const InternalNode *mParent; ++#define BASE(v) BaseT::v ++#endif ++ __global__ const InternalNode *mParent; + public: + __hostdev__ ValueOnIterator() : BaseT(), mParent(nullptr) {} +- __hostdev__ ValueOnIterator(const InternalNode* parent) : BaseT(parent->data()->mValueMask.beginOn()), mParent(parent) {} +- ValueOnIterator& operator=(const ValueOnIterator&) = default; +- __hostdev__ ValueType operator*() const {NANOVDB_ASSERT(*this); return mParent->data()->getValue(BaseT::pos());} +- __hostdev__ CoordType getOrigin() const { NANOVDB_ASSERT(*this); return mParent->localToGlobalCoord(BaseT::pos());} ++ __hostdev__ ValueOnIterator(__global__ const InternalNode* parent) : BaseT(parent->data()->mValueMask.beginOn()), mParent(parent) {} ++ __global__ ValueOnIterator& operator=(__global__ const ValueOnIterator&) = default; ++ __hostdev__ ValueType operator*() const {NANOVDB_ASSERT(*this); return mParent->data()->getValue(BASE(pos)());} ++ __hostdev__ CoordType getOrigin() const { NANOVDB_ASSERT(*this); return mParent->localToGlobalCoord(BASE(pos)());} + }; // Member class ValueOnIterator + + ValueOnIterator beginValueOn() const {return ValueOnIterator(this);} + ++#if defined(__KERNEL_METAL__) ++#define BASE(v) _base.v ++#else ++#define BASE(v) DataType::v ++#endif ++ + /// @brief This class cannot be constructed or deleted +- InternalNode() = delete; +- InternalNode(const InternalNode&) = delete; +- InternalNode& operator=(const InternalNode&) = delete; ++ InternalNode() __global__ = delete; ++ InternalNode(__global__ const InternalNode&) __global__ = delete; ++ __global__ InternalNode& operator=(__global__ const InternalNode&) __global__ = delete; + ~InternalNode() = delete; + +- __hostdev__ DataType* data() { return reinterpret_cast(this); } ++ __hostdev__ __global__ DataType* data() __global__ { return reinterpret_cast<__global__ DataType*>(this); } + +- __hostdev__ const DataType* data() const { return reinterpret_cast(this); } ++ __hostdev__ __global__ const DataType* data() const __global__ { return reinterpret_cast<__global__ const DataType*>(this); } + + /// @brief Return the dimension, in voxel units, of this internal node (typically 8*16 or 8*16*32) + __hostdev__ static uint32_t dim() { return 1u << TOTAL; } +@@ -3605,47 +4161,66 @@ + __hostdev__ static size_t memUsage() { return DataType::memUsage(); } + + /// @brief Return a const reference to the bit mask of active voxels in this internal node +- __hostdev__ const MaskType& valueMask() const { return DataType::mValueMask; } ++ __hostdev__ __global__ const MaskType& valueMask() const __global__ { return BASE(mValueMask); } + + /// @brief Return a const reference to the bit mask of child nodes in this internal node +- __hostdev__ const MaskType& childMask() const { return DataType::mChildMask; } ++ __hostdev__ __global__ const MaskType& childMask() const __global__ { return DataType::mChildMask; } + + /// @brief Return the origin in index space of this leaf node +- __hostdev__ CoordType origin() const { return DataType::mBBox.min() & ~MASK; } ++ __hostdev__ CoordType origin() const __global__ { return DataType::mBBox.min() & ~MASK; } + + /// @brief Return a const reference to the minimum active value encoded in this internal node and any of its child nodes +- __hostdev__ const ValueType& minimum() const { return this->getMin(); } ++ __hostdev__ __global__ const ValueType& minimum() const __global__ { return this->getMin(); } + + /// @brief Return a const reference to the maximum active value encoded in this internal node and any of its child nodes +- __hostdev__ const ValueType& maximum() const { return this->getMax(); } ++ __hostdev__ __global__ const ValueType& maximum() const __global__ { return this->getMax(); } + + /// @brief Return a const reference to the average of all the active values encoded in this internal node and any of its child nodes +- __hostdev__ const FloatType& average() const { return DataType::mAverage; } ++ __hostdev__ __global__ const FloatType& average() const __global__ { return DataType::mAverage; } + + /// @brief Return the variance of all the active values encoded in this internal node and any of its child nodes +- __hostdev__ FloatType variance() const { return DataType::mStdDevi*DataType::mStdDevi; } ++ __hostdev__ FloatType variance() const __global__ { return DataType::mStdDevi*DataType::mStdDevi; } + + /// @brief Return a const reference to the standard deviation of all the active values encoded in this internal node and any of its child nodes +- __hostdev__ const FloatType& stdDeviation() const { return DataType::mStdDevi; } ++ __hostdev__ __global__ const FloatType& stdDeviation() const __global__ { return DataType::mStdDevi; } + + /// @brief Return a const reference to the bounding box in index space of active values in this internal node and any of its child nodes +- __hostdev__ const BBox& bbox() const { return DataType::mBBox; } ++ __hostdev__ __global__ const BBox& bbox() const __global__ { return DataType::mBBox; } + + /// @brief Return the value of the given voxel +- __hostdev__ ValueType getValue(const CoordType& ijk) const ++ __hostdev__ ValueType getValue(__global__ const CoordType& ijk) const __global__ + { + const uint32_t n = CoordToOffset(ijk); + return DataType::mChildMask.isOn(n) ? this->getChild(n)->getValue(ijk) : DataType::getValue(n); + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ ValueType getValue(__local__ const CoordType& ijk) const __global__ ++ { ++ const uint32_t n = CoordToOffset(ijk); ++ return DataType::mChildMask.isOn(n) ? this->getChild(n)->getValue(ijk) : DataType::mTable[n].value; ++ } ++#endif + +- __hostdev__ bool isActive(const CoordType& ijk) const ++ __hostdev__ bool isActive(__global__ const CoordType& ijk) const __global__ + { + const uint32_t n = CoordToOffset(ijk); + return DataType::mChildMask.isOn(n) ? this->getChild(n)->isActive(ijk) : DataType::isActive(n); + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isActive(__local__ const CoordType& ijk) const __global__ ++ { ++ const uint32_t n = CoordToOffset(ijk); ++ return DataType::mChildMask.isOn(n) ? this->getChild(n)->isActive(ijk) : DataType::isActive(n); ++ } ++ __hostdev__ bool isActive(__local__ const CoordType& ijk) const __local__ ++ { ++ const uint32_t n = CoordToOffset(ijk); ++ return DataType::mChildMask.isOn(n) ? this->getChild(n)->isActive(ijk) : DataType::isActive(n); ++ } ++#endif + + /// @brief return the state and updates the value of the specified voxel +- __hostdev__ bool probeValue(const CoordType& ijk, ValueType& v) const ++ __hostdev__ bool probeValue(__global__ const CoordType& ijk, __global__ ValueType& v) const __global__ + { + const uint32_t n = CoordToOffset(ijk); + if (DataType::mChildMask.isOn(n)) +@@ -3653,8 +4228,18 @@ + v = DataType::getValue(n); + return DataType::isActive(n); + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool probeValue(__local__ const CoordType& ijk, __local__ ValueType& v) const __global__ ++ { ++ const uint32_t n = CoordToOffset(ijk); ++ if (DataType::mChildMask.isOn(n)) ++ return this->getChild(n)->probeValue(ijk, v); ++ v = DataType::getValue(n); ++ return DataType::isActive(n); ++ } ++#endif + +- __hostdev__ const LeafNodeType* probeLeaf(const CoordType& ijk) const ++ __hostdev__ __global__ const LeafNodeType* probeLeaf(__global__ const CoordType& ijk) const __global__ + { + const uint32_t n = CoordToOffset(ijk); + if (DataType::mChildMask.isOn(n)) +@@ -3662,7 +4247,7 @@ + return nullptr; + } + +- __hostdev__ const ChildNodeType* probeChild(const CoordType& ijk) const ++ __hostdev__ __global__ const ChildNodeType* probeChild(__global__ const CoordType& ijk) const __global__ + { + const uint32_t n = CoordToOffset(ijk); + return DataType::mChildMask.isOn(n) ? this->getChild(n) : nullptr; +@@ -3669,7 +4254,7 @@ + } + + /// @brief Return the linear offset corresponding to the given coordinate +- __hostdev__ static uint32_t CoordToOffset(const CoordType& ijk) ++ __hostdev__ static uint32_t CoordToOffset(__global__ const CoordType& ijk) + { + #if 0 + return (((ijk[0] & MASK) >> ChildT::TOTAL) << (2 * LOG2DIM)) + +@@ -3681,6 +4266,20 @@ + ((ijk[2] & MASK) >> ChildT::TOTAL); + #endif + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ static uint32_t CoordToOffset(__local__ const CoordType& ijk) ++ { ++#if 0 ++ return (((ijk[0] & MASK) >> ChildT::TOTAL) << (2 * LOG2DIM)) + ++ (((ijk[1] & MASK) >> ChildT::TOTAL) << (LOG2DIM)) + ++ ((ijk[2] & MASK) >> ChildT::TOTAL); ++#else ++ return (((ijk[0] & MASK) >> ChildT::TOTAL) << (2 * LOG2DIM)) | ++ (((ijk[1] & MASK) >> ChildT::TOTAL) << (LOG2DIM)) | ++ ((ijk[2] & MASK) >> ChildT::TOTAL); ++#endif ++ } ++#endif + + /// @return the local coordinate of the n'th tile or child node + __hostdev__ static Coord OffsetToLocalCoord(uint32_t n) +@@ -3691,13 +4290,13 @@ + } + + /// @brief modifies local coordinates to global coordinates of a tile or child node +- __hostdev__ void localToGlobalCoord(Coord& ijk) const ++ __hostdev__ void localToGlobalCoord(__global__ Coord& ijk) const __global__ + { + ijk <<= ChildT::TOTAL; + ijk += this->origin(); + } + +- __hostdev__ Coord offsetToGlobalCoord(uint32_t n) const ++ __hostdev__ Coord offsetToGlobalCoord(uint32_t n) const __global__ + { + Coord ijk = InternalNode::OffsetToLocalCoord(n); + this->localToGlobalCoord(ijk); +@@ -3705,13 +4304,24 @@ + } + + /// @brief Return true if this node or any of its child nodes contain active values +- __hostdev__ bool isActive() const ++ __hostdev__ bool isActive() const __global__ + { + return DataType::mFlags & uint32_t(2); + } ++#if defined(__KERNEL_METAL__) ++ /// @brief Retrun true if this node or any of its child nodes contain active values ++ __hostdev__ bool isActive() const __local__ ++ { ++ return DataType::mFlags & uint32_t(2); ++ } ++#endif + + private: ++#if !defined(__KERNEL_METAL__) + static_assert(sizeof(DataType) % NANOVDB_DATA_ALIGNMENT == 0, "sizeof(InternalData) is misaligned"); ++#else ++ static_assert(sizeof(_base) % NANOVDB_DATA_ALIGNMENT == 0, "sizeof(InternalData) is misaligned"); ++#endif + //static_assert(offsetof(DataType, mTable) % 32 == 0, "InternalData::mTable is misaligned"); + + template +@@ -3724,18 +4334,30 @@ + + /// @brief Private read access method used by the ReadAccessor + template +- __hostdev__ ValueType getValueAndCache(const CoordType& ijk, const AccT& acc) const ++ __hostdev__ ValueType getValueAndCache(__global__ const CoordType& ijk, __global__ const AccT& acc) const __global__ + { + const uint32_t n = CoordToOffset(ijk); + if (!DataType::mChildMask.isOn(n)) +- return DataType::getValue(n); +- const ChildT* child = this->getChild(n); ++ return BASE(getValue)(n); ++ __global__ const ChildT* child = BASE(getChild)(n); + acc.insert(ijk, child); + return child->getValueAndCache(ijk, acc); + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ ValueType getValueAndCache(__local__ const CoordType& ijk, __local__ const AccT& acc) const __global__ ++ { ++ const uint32_t n = CoordToOffset(ijk); ++ if (!BASE(mChildMask).isOn(n)) ++ return BASE(getValue)(n); ++ __global__ const ChildT* child = BASE(getChild)(n); ++ acc.insert(ijk, child); ++ return child->getValueAndCache(ijk, acc); ++ } ++#endif + + template +- __hostdev__ typename AccT::NodeInfo getNodeInfoAndCache(const CoordType& ijk, const AccT& acc) const ++ __hostdev__ typename AccT::NodeInfo getNodeInfoAndCache(__global__ const CoordType& ijk, __global__ const AccT& acc) const __global__ + { + using NodeInfoT = typename AccT::NodeInfo; + const uint32_t n = CoordToOffset(ijk); +@@ -3743,24 +4365,36 @@ + return NodeInfoT{LEVEL, this->dim(), this->minimum(), this->maximum(), this->average(), + this->stdDeviation(), this->bbox()[0], this->bbox()[1]}; + } +- const ChildT* child = this->getChild(n); ++ __global__ const ChildT* child = BASE(getChild)(n); + acc.insert(ijk, child); + return child->getNodeInfoAndCache(ijk, acc); + } + + template +- __hostdev__ bool isActiveAndCache(const CoordType& ijk, const AccT& acc) const ++ __hostdev__ bool isActiveAndCache(__global__ const CoordType& ijk, __global__ const AccT& acc) const __global__ + { + const uint32_t n = CoordToOffset(ijk); + if (!DataType::mChildMask.isOn(n)) + return DataType::isActive(n); +- const ChildT* child = this->getChild(n); ++ __global__ const ChildT* child = BASE(getChild)(n); + acc.insert(ijk, child); + return child->isActiveAndCache(ijk, acc); + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ bool isActiveAndCache(__local__ const CoordType& ijk, __local__ const AccT& acc) const __global__ ++ { ++ const uint32_t n = CoordToOffset(ijk); ++ if (!BASE(mChildMask).isOn(n)) ++ return BASE(mValueMask).isOn(n); ++ __global__ const ChildT* child = BASE(getChild)(n); ++ acc.insert(ijk, child); ++ return child->isActiveAndCache(ijk, acc); ++ } ++#endif + + template +- __hostdev__ bool probeValueAndCache(const CoordType& ijk, ValueType& v, const AccT& acc) const ++ __hostdev__ bool probeValueAndCache(__global__ const CoordType& ijk, __global__ ValueType& v, __global__ const AccT& acc) const __global__ + { + const uint32_t n = CoordToOffset(ijk); + if (!DataType::mChildMask.isOn(n)) { +@@ -3767,24 +4401,24 @@ + v = DataType::getValue(n); + return DataType::isActive(n); + } +- const ChildT* child = this->getChild(n); ++ __global__ const ChildT* child = BASE(getChild)(n); + acc.insert(ijk, child); + return child->probeValueAndCache(ijk, v, acc); + } + + template +- __hostdev__ const LeafNodeType* probeLeafAndCache(const CoordType& ijk, const AccT& acc) const ++ __hostdev__ __global__ const LeafNodeType* probeLeafAndCache(__global__ const CoordType& ijk, __global__ const AccT& acc) const __global__ + { + const uint32_t n = CoordToOffset(ijk); + if (!DataType::mChildMask.isOn(n)) + return nullptr; +- const ChildT* child = this->getChild(n); ++ __global__ const ChildT* child = BASE(getChild)(n); + acc.insert(ijk, child); + return child->probeLeafAndCache(ijk, acc); + } + + template +- __hostdev__ uint32_t getDimAndCache(const CoordType& ijk, const RayT& ray, const AccT& acc) const ++ __hostdev__ uint32_t getDimAndCache(__global__ const CoordType& ijk, __global__ const RayT& ray, __global__ const AccT& acc) const __global__ + { + if (DataType::mFlags & uint32_t(1u)) return this->dim(); // skip this node if the 1st bit is set + //if (!ray.intersects( this->bbox() )) return 1<getChild(n); ++ __global__ const ChildT* child = BASE(getChild)(n); + acc.insert(ijk, child); + return child->getDimAndCache(ijk, ray, acc); + } + return ChildNodeType::dim(); // tile value + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ uint32_t getDimAndCache(__local__ const CoordType& ijk, __local__ const RayT& ray, __local__ const AccT& acc) const __global__ ++ { ++ if (BASE(mFlags) & uint32_t(1)) ++ this->dim(); //ship this node if first bit is set ++ //if (!ray.intersects( this->bbox() )) return 1<getDimAndCache(ijk, ray, acc); ++ } ++ return ChildNodeType::dim(); // tile value ++ } ++#endif ++ ++#undef BASE + }; // InternalNode class + + // --------------------------> LeafNode <------------------------------------ +@@ -3814,7 +4466,7 @@ + using BuildType = ValueT; + using FloatType = typename FloatTraits::FloatType; + using ArrayType = ValueT;// type used for the internal mValue array +- static constexpr bool FIXED_SIZE = true; ++ static __constant__ constexpr bool FIXED_SIZE = true; + + CoordT mBBoxMin; // 12B. + uint8_t mBBoxDif[3]; // 3B. +@@ -3826,7 +4478,7 @@ + FloatType mAverage; // typically 4B, average of all the active values in this node and its child nodes + FloatType mStdDevi; // typically 4B, standard deviation of all the active values in this node and its child nodes + alignas(32) ValueType mValues[1u << 3 * LOG2DIM]; +- ++ + /// @brief Return padding of this class in bytes, due to aliasing and 32B alignment + /// + /// @note The extra bytes are not necessarily at the end, but can come from aliasing of individual data members. +@@ -3838,32 +4490,35 @@ + __hostdev__ static uint64_t memUsage() { return sizeof(LeafData); } + + //__hostdev__ const ValueType* values() const { return mValues; } +- __hostdev__ ValueType getValue(uint32_t i) const { return mValues[i]; } +- __hostdev__ void setValueOnly(uint32_t offset, const ValueType& value) { mValues[offset] = value; } +- __hostdev__ void setValue(uint32_t offset, const ValueType& value) ++ __hostdev__ ValueType getValue(uint32_t i) const __global__ { return mValues[i]; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ ValueType getValue(uint32_t i) const __local__ { return mValues[i]; } ++#endif ++ __hostdev__ void setValueOnly(uint32_t offset, __global__ const ValueType& value) __global__ { mValues[offset] = value; } ++ __hostdev__ void setValue(uint32_t offset, __global__ const ValueType& value) __global__ + { + mValueMask.setOn(offset); + mValues[offset] = value; + } + +- __hostdev__ ValueType getMin() const { return mMinimum; } +- __hostdev__ ValueType getMax() const { return mMaximum; } +- __hostdev__ FloatType getAvg() const { return mAverage; } +- __hostdev__ FloatType getDev() const { return mStdDevi; } ++ __hostdev__ ValueType getMin() const __global__ { return mMinimum; } ++ __hostdev__ ValueType getMax() const __global__ { return mMaximum; } ++ __hostdev__ FloatType getAvg() const __global__ { return mAverage; } ++ __hostdev__ FloatType getDev() const __global__ { return mStdDevi; } + +- __hostdev__ void setMin(const ValueType& v) { mMinimum = v; } +- __hostdev__ void setMax(const ValueType& v) { mMaximum = v; } +- __hostdev__ void setAvg(const FloatType& v) { mAverage = v; } +- __hostdev__ void setDev(const FloatType& v) { mStdDevi = v; } ++ __hostdev__ void setMin(__global__ const ValueType& v) __global__ { mMinimum = v; } ++ __hostdev__ void setMax(__global__ const ValueType& v) __global__ { mMaximum = v; } ++ __hostdev__ void setAvg(__global__ const FloatType& v) __global__ { mAverage = v; } ++ __hostdev__ void setDev(__global__ const FloatType& v) __global__ { mStdDevi = v; } + + template +- __hostdev__ void setOrigin(const T& ijk) { mBBoxMin = ijk; } ++ __hostdev__ void setOrigin(__global__ const T& ijk) __global__ { mBBoxMin = ijk; } + + /// @brief This class cannot be constructed or deleted +- LeafData() = delete; +- LeafData(const LeafData&) = delete; +- LeafData& operator=(const LeafData&) = delete; +- ~LeafData() = delete; ++ LeafData() __global__ = delete; ++ LeafData(__global__ const LeafData&) __global__ = delete; ++ __global__ LeafData& operator=(__global__ const LeafData&) __global__ = delete; ++ ~LeafData() __global__ = delete; + }; // LeafData + + /// @brief Base-class for quantized float leaf nodes +@@ -3892,7 +4547,7 @@ + __hostdev__ static constexpr uint32_t padding() { + return sizeof(LeafFnBase) - (12 + 3 + 1 + sizeof(MaskT) + 2*4 + 4*2); + } +- __hostdev__ void init(float min, float max, uint8_t bitWidth) ++ __hostdev__ void init(float min, float max, uint8_t bitWidth) __global__ + { + mMinimum = min; + mQuantum = (max - min)/float((1 << bitWidth)-1); +@@ -3899,32 +4554,32 @@ + } + + /// @brief return the quantized minimum of the active values in this node +- __hostdev__ float getMin() const { return mMin*mQuantum + mMinimum; } ++ __hostdev__ float getMin() const __global__ { return mMin*mQuantum + mMinimum; } + + /// @brief return the quantized maximum of the active values in this node +- __hostdev__ float getMax() const { return mMax*mQuantum + mMinimum; } ++ __hostdev__ float getMax() const __global__ { return mMax*mQuantum + mMinimum; } + + /// @brief return the quantized average of the active values in this node +- __hostdev__ float getAvg() const { return mAvg*mQuantum + mMinimum; } ++ __hostdev__ float getAvg() const __global__ { return mAvg*mQuantum + mMinimum; } + /// @brief return the quantized standard deviation of the active values in this node + + /// @note 0 <= StdDev <= max-min or 0 <= StdDev/(max-min) <= 1 +- __hostdev__ float getDev() const { return mDev*mQuantum; } ++ __hostdev__ float getDev() const __global__ { return mDev*mQuantum; } + + /// @note min <= X <= max or 0 <= (X-min)/(min-max) <= 1 +- __hostdev__ void setMin(float min) { mMin = uint16_t((min - mMinimum)/mQuantum + 0.5f); } ++ __hostdev__ void setMin(float min) __global__ { mMin = uint16_t((min - mMinimum)/mQuantum + 0.5f); } + + /// @note min <= X <= max or 0 <= (X-min)/(min-max) <= 1 +- __hostdev__ void setMax(float max) { mMax = uint16_t((max - mMinimum)/mQuantum + 0.5f); } ++ __hostdev__ void setMax(float max) __global__ { mMax = uint16_t((max - mMinimum)/mQuantum + 0.5f); } + + /// @note min <= avg <= max or 0 <= (avg-min)/(min-max) <= 1 +- __hostdev__ void setAvg(float avg) { mAvg = uint16_t((avg - mMinimum)/mQuantum + 0.5f); } ++ __hostdev__ void setAvg(float avg) __global__ { mAvg = uint16_t((avg - mMinimum)/mQuantum + 0.5f); } + + /// @note 0 <= StdDev <= max-min or 0 <= StdDev/(max-min) <= 1 +- __hostdev__ void setDev(float dev) { mDev = uint16_t(dev/mQuantum + 0.5f); } ++ __hostdev__ void setDev(float dev) __global__ { mDev = uint16_t(dev/mQuantum + 0.5f); } + + template +- __hostdev__ void setOrigin(const T& ijk) { mBBoxMin = ijk; } ++ __hostdev__ void setOrigin(__global__ const T& ijk) __global__ { mBBoxMin = ijk; } + };// LeafFnBase + + /// @brief Stuct with all the member data of the LeafNode (useful during serialization of an openvdb LeafNode) +@@ -3932,12 +4587,24 @@ + /// @note No client code should (or can) interface with this struct so it can safely be ignored! + template class MaskT, uint32_t LOG2DIM> + struct NANOVDB_ALIGN(NANOVDB_DATA_ALIGNMENT) LeafData ++#if !defined(__KERNEL_METAL__) + : public LeafFnBase ++#endif + { ++#if defined(__KERNEL_METAL__) ++ LeafFnBase _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) BaseT::v ++#endif + using BaseT = LeafFnBase; + using BuildType = Fp4; + using ArrayType = uint8_t;// type used for the internal mValue array +- static constexpr bool FIXED_SIZE = true; ++#if defined(__KERNEL_METAL__) ++ using ValueType = typename BaseT::ValueType; ++ using FloatType = typename BaseT::FloatType; ++#endif ++ static __constant__ constexpr bool FIXED_SIZE = true; + alignas(32) uint8_t mCode[1u << (3 * LOG2DIM - 1)];// LeafFnBase is 32B aligned and so is mCode + + __hostdev__ static constexpr uint64_t memUsage() { return sizeof(LeafData); } +@@ -3947,31 +4614,53 @@ + } + + __hostdev__ static constexpr uint8_t bitWidth() { return 4u; } +- __hostdev__ float getValue(uint32_t i) const ++#if defined(__KERNEL_METAL__) ++ __hostdev__ float getValue(uint32_t i) const __global__ + { + #if 0 + const uint8_t c = mCode[i>>1]; + return ( (i&1) ? c >> 4 : c & uint8_t(15) )*BaseT::mQuantum + BaseT::mMinimum; + #else +- return ((mCode[i>>1] >> ((i&1)<<2)) & uint8_t(15))*BaseT::mQuantum + BaseT::mMinimum; ++ return ((mCode[i>>1] >> ((i&1)<<2)) & uint8_t(15))*BASE(mQuantum) + BASE(mMinimum); + #endif + } ++#endif ++#if defined(__KERNEL_METAL__) ++__hostdev__ float getValue(uint32_t i) const __local__ ++ { ++#if 0 ++ const uint8_t c = mCode[i>>1]; ++ return ( (i&1) ? c >> 4 : c & uint8_t(15) )*BaseT::mQuantum + BaseT::mMinimum; ++#else ++ return ((mCode[i>>1] >> ((i&1)<<2)) & uint8_t(15))*BASE(mQuantum) + BASE(mMinimum); ++#endif ++ } ++#endif + + /// @brief This class cannot be constructed or deleted +- LeafData() = delete; +- LeafData(const LeafData&) = delete; +- LeafData& operator=(const LeafData&) = delete; +- ~LeafData() = delete; ++ LeafData() __global__ = delete; ++ LeafData(__global__ const LeafData&) __global__ = delete; ++ __global__ LeafData& operator=(__global__ const LeafData&) __global__ = delete; ++ ~LeafData() __global__ = delete; ++#undef BASE + }; // LeafData + + template class MaskT, uint32_t LOG2DIM> + struct NANOVDB_ALIGN(NANOVDB_DATA_ALIGNMENT) LeafData ++#if !defined(__KERNEL_METAL__) + : public LeafFnBase ++#endif + { ++#if defined(__KERNEL_METAL__) ++ LeafFnBase _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) BaseT::v ++#endif + using BaseT = LeafFnBase; + using BuildType = Fp8; + using ArrayType = uint8_t;// type used for the internal mValue array +- static constexpr bool FIXED_SIZE = true; ++ static __constant__ constexpr bool FIXED_SIZE = true; + alignas(32) uint8_t mCode[1u << 3 * LOG2DIM]; + __hostdev__ static constexpr int64_t memUsage() { return sizeof(LeafData); } + __hostdev__ static constexpr uint32_t padding() { +@@ -3980,25 +4669,44 @@ + } + + __hostdev__ static constexpr uint8_t bitWidth() { return 8u; } +- __hostdev__ float getValue(uint32_t i) const ++ __hostdev__ float getValue(uint32_t i) const __global__ + { + return mCode[i]*BaseT::mQuantum + BaseT::mMinimum;// code * (max-min)/255 + min + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ float getValue(uint32_t i) const __local__ ++ { ++ return mCode[i]*BaseT::mQuantum + BaseT::mMinimum;// code * (max-min)/255 + min ++ } ++#endif + /// @brief This class cannot be constructed or deleted +- LeafData() = delete; +- LeafData(const LeafData&) = delete; +- LeafData& operator=(const LeafData&) = delete; +- ~LeafData() = delete; ++ LeafData() __global__ = delete; ++ LeafData(__global__ const LeafData&) __global__ = delete; ++ __global__ LeafData& operator=(__global__ const LeafData&) __global__ = delete; ++ ~LeafData() __global__ = delete; ++#undef BASE + }; // LeafData + + template class MaskT, uint32_t LOG2DIM> + struct NANOVDB_ALIGN(NANOVDB_DATA_ALIGNMENT) LeafData ++#if !defined(__KERNEL_METAL__) + : public LeafFnBase ++#endif + { ++#if defined(__KERNEL_METAL__) ++ LeafFnBase _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) BaseT::v ++#endif + using BaseT = LeafFnBase; + using BuildType = Fp16; + using ArrayType = uint16_t;// type used for the internal mValue array +- static constexpr bool FIXED_SIZE = true; ++#if defined(__KERNEL_METAL__) ++ using ValueType = typename BaseT::ValueType; ++ using FloatType = typename BaseT::FloatType; ++#endif ++ static __constant__ constexpr bool FIXED_SIZE = true; + alignas(32) uint16_t mCode[1u << 3 * LOG2DIM]; + + __hostdev__ static constexpr uint64_t memUsage() { return sizeof(LeafData); } +@@ -4008,37 +4716,95 @@ + } + + __hostdev__ static constexpr uint8_t bitWidth() { return 16u; } +- __hostdev__ float getValue(uint32_t i) const ++ __hostdev__ float getValue(uint32_t i) const __global__ + { +- return mCode[i]*BaseT::mQuantum + BaseT::mMinimum;// code * (max-min)/65535 + min ++ return mCode[i]*BASE(mQuantum) + BASE(mMinimum);// code * (max-min)/65535 + min + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ float getValue(uint32_t i) const __local__ ++ { ++ return mCode[i]*BaseT::Quantum + BaseT::mMinimum;// code * (max-min)/65535 + min ++ } ++#endif + + /// @brief This class cannot be constructed or deleted +- LeafData() = delete; +- LeafData(const LeafData&) = delete; +- LeafData& operator=(const LeafData&) = delete; +- ~LeafData() = delete; ++ LeafData() __global__ = delete; ++ LeafData(__global__ const LeafData&) __global__ = delete; ++ __global__ LeafData& operator=(__global__ const LeafData&) __global__ = delete; ++ ~LeafData() __global__ = delete; ++#undef BASE + }; // LeafData + + template class MaskT, uint32_t LOG2DIM> + struct NANOVDB_ALIGN(NANOVDB_DATA_ALIGNMENT) LeafData ++#if !defined(__KERNEL_METAL__) + : public LeafFnBase ++#endif + {// this class has no data members, however every instance is immediately followed + // bitWidth*64 bytes. Since its base class is 32B aligned so are the bitWidth*64 bytes ++#if defined(__KERNEL_METAL__) ++ LeafFnBase _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) BaseT::v ++#endif + using BaseT = LeafFnBase; + using BuildType = FpN; +- static constexpr bool FIXED_SIZE = false; ++ static __constant__ constexpr bool FIXED_SIZE = false; ++#if defined(__KERNEL_METAL__) ++ using ValueType = typename BaseT::ValueType; ++ using FloatType = typename BaseT::FloatType; ++#endif + __hostdev__ static constexpr uint32_t padding() { + static_assert(BaseT::padding()==0, "expected no padding in LeafFnBase"); + return 0; + } + +- __hostdev__ uint8_t bitWidth() const { return 1 << (BaseT::mFlags >> 5); }// 4,8,16,32 = 2^(2,3,4,5) +- __hostdev__ size_t memUsage() const { return sizeof(*this) + this->bitWidth()*64; } ++ __hostdev__ uint8_t bitWidth() const __global__ { return 1 << (BaseT::mFlags >> 5); }// 4,8,16,32 = 2^(2,3,4,5) ++ __hostdev__ size_t memUsage() const __global__ { return sizeof(*this) + this->bitWidth()*64; } + __hostdev__ static size_t memUsage(uint32_t bitWidth) { return 96u + bitWidth*64; } +- __hostdev__ float getValue(uint32_t i) const ++ __hostdev__ float getValue(uint32_t i) const __global__ + { + #ifdef NANOVDB_FPN_BRANCHLESS// faster ++ const int b = BASE(mFlags) >> 5;// b = 0, 1, 2, 3, 4 corresponding to 1, 2, 4, 8, 16 bits ++#if 0// use LUT ++ uint16_t code = reinterpret_cast(this + 1)[i >> (4 - b)]; ++ const static uint8_t shift[5] = {15, 7, 3, 1, 0}; ++ const static uint16_t mask[5] = {1, 3, 15, 255, 65535}; ++ code >>= (i & shift[b]) << b; ++ code &= mask[b]; ++#else// no LUT ++ uint32_t code = reinterpret_cast<__global__ const uint32_t*>(this + 1)[i >> (5 - b)]; ++ //code >>= (i & ((16 >> b) - 1)) << b; ++ code >>= (i & ((32 >> b) - 1)) << b; ++ code &= (1 << (1 << b)) - 1; ++#endif ++#else// use branched version (slow) ++ float code; ++ __global__ auto *values = reinterpret_cast(this+1); ++ switch (BaseT::mFlags >> 5) { ++ case 0u:// 1 bit float ++ code = float((values[i>>3] >> (i&7) ) & uint8_t(1)); ++ break; ++ case 1u:// 2 bits float ++ code = float((values[i>>2] >> ((i&3)<<1)) & uint8_t(3)); ++ break; ++ case 2u:// 4 bits float ++ code = float((values[i>>1] >> ((i&1)<<2)) & uint8_t(15)); ++ break; ++ case 3u:// 8 bits float ++ code = float(values[i]); ++ break; ++ default:// 16 bits float ++ code = float(reinterpret_cast(values)[i]); ++ } ++#endif ++ return float(code) * BASE(mQuantum) + BASE(mMinimum);// code * (max-min)/UNITS + min ++ } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ float getValue(uint32_t i) const __local__ ++ { ++#ifdef NANOVDB_FPN_BRANCHLESS// faster + const int b = BaseT::mFlags >> 5;// b = 0, 1, 2, 3, 4 corresponding to 1, 2, 4, 8, 16 bits + #if 0// use LUT + uint16_t code = reinterpret_cast(this + 1)[i >> (4 - b)]; +@@ -4047,7 +4813,7 @@ + code >>= (i & shift[b]) << b; + code &= mask[b]; + #else// no LUT +- uint32_t code = reinterpret_cast(this + 1)[i >> (5 - b)]; ++ uint32_t code = reinterpret_cast<__global__ const uint32_t*>(this + 1)[i >> (5 - b)]; + //code >>= (i & ((16 >> b) - 1)) << b; + code >>= (i & ((32 >> b) - 1)) << b; + code &= (1 << (1 << b)) - 1; +@@ -4054,7 +4820,7 @@ + #endif + #else// use branched version (slow) + float code; +- auto *values = reinterpret_cast(this+1); ++ __global__ auto *values = reinterpret_cast(this+1); + switch (BaseT::mFlags >> 5) { + case 0u:// 1 bit float + code = float((values[i>>3] >> (i&7) ) & uint8_t(1)); +@@ -4074,12 +4840,15 @@ + #endif + return float(code) * BaseT::mQuantum + BaseT::mMinimum;// code * (max-min)/UNITS + min + } ++#endif + + /// @brief This class cannot be constructed or deleted +- LeafData() = delete; +- LeafData(const LeafData&) = delete; +- LeafData& operator=(const LeafData&) = delete; +- ~LeafData() = delete; ++ LeafData() __global__ = delete; ++ LeafData(__global__ const LeafData&) __global__ = delete; ++ __global__ LeafData& operator=(__global__ const LeafData&) __global__ = delete; ++ ~LeafData() __global__ = delete; ++ ++#undef BASE + }; // LeafData + + // Partial template specialization of LeafData with bool +@@ -4092,7 +4861,7 @@ + using BuildType = bool; + using FloatType = bool;// dummy value type + using ArrayType = MaskT;// type used for the internal mValue array +- static constexpr bool FIXED_SIZE = true; ++ static __constant__ constexpr bool FIXED_SIZE = true; + + CoordT mBBoxMin; // 12B. + uint8_t mBBoxDif[3]; // 3B. +@@ -4104,31 +4873,34 @@ + __hostdev__ static constexpr uint32_t padding() {return sizeof(LeafData) - 12u - 3u - 1u - 2*sizeof(MaskT) - 16u;} + __hostdev__ static uint64_t memUsage() { return sizeof(LeafData); } + +- //__hostdev__ const ValueType* values() const { return nullptr; } +- __hostdev__ bool getValue(uint32_t i) const { return mValues.isOn(i); } +- __hostdev__ bool getMin() const { return false; }// dummy +- __hostdev__ bool getMax() const { return false; }// dummy +- __hostdev__ bool getAvg() const { return false; }// dummy +- __hostdev__ bool getDev() const { return false; }// dummy +- __hostdev__ void setValue(uint32_t offset, bool v) ++ //__hostdev__ __global__ const ValueType* values() const __global__ { return nullptr; } ++ __hostdev__ bool getValue(uint32_t i) const __global__ { return mValues.isOn(i); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool getValue(uint32_t i) const __local__ { return mValues.isOn(i); } ++#endif ++ __hostdev__ bool getMin() const __global__ { return false; }// dummy ++ __hostdev__ bool getMax() const __global__ { return false; }// dummy ++ __hostdev__ bool getAvg() const __global__ { return false; }// dummy ++ __hostdev__ bool getDev() const __global__ { return false; }// dummy ++ __hostdev__ void setValue(uint32_t offset, bool v) __global__ + { + mValueMask.setOn(offset); + mValues.set(offset, v); + } + +- __hostdev__ void setMin(const bool&) {}// no-op +- __hostdev__ void setMax(const bool&) {}// no-op +- __hostdev__ void setAvg(const bool&) {}// no-op +- __hostdev__ void setDev(const bool&) {}// no-op ++ __hostdev__ void setMin(__global__ const bool&) __global__ {}// no-op ++ __hostdev__ void setMax(__global__ const bool&) __global__ {}// no-op ++ __hostdev__ void setAvg(__global__ const bool&) __global__ {}// no-op ++ __hostdev__ void setDev(__global__ const bool&) __global__ {}// no-op + + template +- __hostdev__ void setOrigin(const T& ijk) { mBBoxMin = ijk; } ++ __hostdev__ void setOrigin(__global__ const T& ijk) __global__ { mBBoxMin = ijk; } + + /// @brief This class cannot be constructed or deleted +- LeafData() = delete; +- LeafData(const LeafData&) = delete; +- LeafData& operator=(const LeafData&) = delete; +- ~LeafData() = delete; ++ LeafData() __global__ = delete; ++ LeafData(__global__ const LeafData&) __global__ = delete; ++ __global__ LeafData& operator=(__global__ const LeafData&) __global__ = delete; ++ ~LeafData() __global__ = delete; + }; // LeafData + + // Partial template specialization of LeafData with ValueMask +@@ -4141,7 +4913,7 @@ + using BuildType = ValueMask; + using FloatType = bool;// dummy value type + using ArrayType = void;// type used for the internal mValue array - void means missing +- static constexpr bool FIXED_SIZE = true; ++ static __constant__ constexpr bool FIXED_SIZE = true; + + CoordT mBBoxMin; // 12B. + uint8_t mBBoxDif[3]; // 3B. +@@ -4156,29 +4928,32 @@ + } + + //__hostdev__ const ValueType* values() const { return nullptr; } +- __hostdev__ bool getValue(uint32_t i) const { return mValueMask.isOn(i); } +- __hostdev__ bool getMin() const { return false; }// dummy +- __hostdev__ bool getMax() const { return false; }// dummy +- __hostdev__ bool getAvg() const { return false; }// dummy +- __hostdev__ bool getDev() const { return false; }// dummy +- __hostdev__ void setValue(uint32_t offset, bool) ++ __hostdev__ bool getValue(uint32_t i) const __global__ { return mValueMask.isOn(i); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool getValue(uint32_t i) const __local__ { return mValueMask.isOn(i); } ++#endif ++ __hostdev__ bool getMin() const __global__ { return false; }// dummy ++ __hostdev__ bool getMax() const __global__ { return false; }// dummy ++ __hostdev__ bool getAvg() const __global__ { return false; }// dummy ++ __hostdev__ bool getDev() const __global__ { return false; }// dummy ++ __hostdev__ void setValue(uint32_t offset, bool) __global__ + { + mValueMask.setOn(offset); + } + +- __hostdev__ void setMin(const ValueType&) {}// no-op +- __hostdev__ void setMax(const ValueType&) {}// no-op +- __hostdev__ void setAvg(const FloatType&) {}// no-op +- __hostdev__ void setDev(const FloatType&) {}// no-op ++ __hostdev__ void setMin(__global__ const ValueType&) __global__ {}// no-op ++ __hostdev__ void setMax(__global__ const ValueType&) __global__ {}// no-op ++ __hostdev__ void setAvg(__global__ const FloatType&) __global__ {}// no-op ++ __hostdev__ void setDev(__global__ const FloatType&) __global__ {}// no-op + + template +- __hostdev__ void setOrigin(const T& ijk) { mBBoxMin = ijk; } ++ __hostdev__ void setOrigin(__global__ const T& ijk) __global__ { mBBoxMin = ijk; } + + /// @brief This class cannot be constructed or deleted +- LeafData() = delete; +- LeafData(const LeafData&) = delete; +- LeafData& operator=(const LeafData&) = delete; +- ~LeafData() = delete; ++ LeafData() __global__ = delete; ++ LeafData(__global__ const LeafData&) __global__ = delete; ++ __global__ LeafData& operator=(__global__ const LeafData&) __global__ = delete; ++ ~LeafData() __global__ = delete; + }; // LeafData + + // Partial template specialization of LeafData with ValueIndex +@@ -4191,7 +4966,7 @@ + using BuildType = ValueIndex; + using FloatType = uint64_t; + using ArrayType = void;// type used for the internal mValue array - void means missing +- static constexpr bool FIXED_SIZE = true; ++ static __constant__ constexpr bool FIXED_SIZE = true; + + CoordT mBBoxMin; // 12B. + uint8_t mBBoxDif[3]; // 3B. +@@ -4208,16 +4983,16 @@ + + __hostdev__ static uint64_t memUsage() { return sizeof(LeafData); } + +- __hostdev__ uint64_t getMin() const { NANOVDB_ASSERT(mStatsOff); return mStatsOff + 0; } +- __hostdev__ uint64_t getMax() const { NANOVDB_ASSERT(mStatsOff); return mStatsOff + 1; } +- __hostdev__ uint64_t getAvg() const { NANOVDB_ASSERT(mStatsOff); return mStatsOff + 2; } +- __hostdev__ uint64_t getDev() const { NANOVDB_ASSERT(mStatsOff); return mStatsOff + 3; } ++ __hostdev__ uint64_t getMin() const __global__ { NANOVDB_ASSERT(mStatsOff); return mStatsOff + 0; } ++ __hostdev__ uint64_t getMax() const __global__ { NANOVDB_ASSERT(mStatsOff); return mStatsOff + 1; } ++ __hostdev__ uint64_t getAvg() const __global__ { NANOVDB_ASSERT(mStatsOff); return mStatsOff + 2; } ++ __hostdev__ uint64_t getDev() const __global__ { NANOVDB_ASSERT(mStatsOff); return mStatsOff + 3; } + __hostdev__ void setValue(uint32_t offset, uint64_t) + { + mValueMask.setOn(offset); + } + +- __hostdev__ uint64_t getValue(uint32_t i) const ++ __hostdev__ uint64_t getValue(uint32_t i) const __global__ + { + if (mFlags & uint8_t(16u)) {// if 4th bit is set only active voxels are indexed + return mValueMask.isOn(i) ? mValueOff + mValueMask.countOn(i) : 0;// 0 is background +@@ -4224,23 +4999,31 @@ + } + return mValueOff + i;// dense array of active and inactive voxels + } +- ++#if defined(__KERNEL_METAL__) ++ __hostdev__ uint64_t getValue(uint32_t i) const __local__ ++ { ++ if (mFlags & uint8_t(16u)) {// if 4th bit is set only active voxels are indexed ++ return mValueMask.isOn(i) ? mValueOff + mValueMask.countOn(i) : 0;// 0 is background ++ } ++ return mValueOff + i;// dense array of active and inactive voxels ++ } ++#endif + template +- __hostdev__ void setMin(const T &min, T *p) { NANOVDB_ASSERT(mStatsOff); p[mStatsOff + 0] = min; } ++ __hostdev__ void setMin(__global__ const T &min, __global__ T *p) __global__ { NANOVDB_ASSERT(mStatsOff); p[mStatsOff + 0] = min; } + template +- __hostdev__ void setMax(const T &max, T *p) { NANOVDB_ASSERT(mStatsOff); p[mStatsOff + 1] = max; } ++ __hostdev__ void setMax(__global__ const T &max, __global__ T *p) __global__ { NANOVDB_ASSERT(mStatsOff); p[mStatsOff + 1] = max; } + template +- __hostdev__ void setAvg(const T &avg, T *p) { NANOVDB_ASSERT(mStatsOff); p[mStatsOff + 2] = avg; } ++ __hostdev__ void setAvg(__global__ const T &avg, __global__ T *p) __global__ { NANOVDB_ASSERT(mStatsOff); p[mStatsOff + 2] = avg; } + template +- __hostdev__ void setDev(const T &dev, T *p) { NANOVDB_ASSERT(mStatsOff); p[mStatsOff + 3] = dev; } ++ __hostdev__ void setDev(__global__ const T &dev, __global__ T *p) __global__ { NANOVDB_ASSERT(mStatsOff); p[mStatsOff + 3] = dev; } + template +- __hostdev__ void setOrigin(const T &ijk) { mBBoxMin = ijk; } ++ __hostdev__ void setOrigin(__global__ const T &ijk) __global__ { mBBoxMin = ijk; } + + /// @brief This class cannot be constructed or deleted +- LeafData() = delete; +- LeafData(const LeafData&) = delete; +- LeafData& operator=(const LeafData&) = delete; +- ~LeafData() = delete; ++ LeafData() __global__ = delete; ++ LeafData(__global__ const LeafData&) __global__ = delete; ++ __global__ LeafData& operator=(__global__ const LeafData&) __global__ = delete; ++ ~LeafData() __global__ = delete; + }; // LeafData + + /// @brief Leaf nodes of the VDB tree. (defaults to 8x8x8 = 512 voxels) +@@ -4248,13 +5031,22 @@ + typename CoordT = Coord, + template class MaskT = Mask, + uint32_t Log2Dim = 3> +-class LeafNode : private LeafData ++class LeafNode ++#if !defined(__KERNEL_METAL__) ++ : private LeafData ++#endif + { ++#if defined(__KERNEL_METAL__) ++ LeafData _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) DataType::v ++#endif + public: + struct ChildNodeType + { +- static constexpr uint32_t TOTAL = 0; +- static constexpr uint32_t DIM = 1; ++ static __constant__ constexpr uint32_t TOTAL = 0; ++ static __constant__ constexpr uint32_t DIM = 1; + __hostdev__ static uint32_t dim() { return 1u; } + }; // Voxel + using LeafNodeType = LeafNode; +@@ -4263,7 +5055,7 @@ + using FloatType = typename DataType::FloatType; + using BuildType = typename DataType::BuildType; + using CoordType = CoordT; +- static constexpr bool FIXED_SIZE = DataType::FIXED_SIZE; ++ static __constant__ constexpr bool FIXED_SIZE = DataType::FIXED_SIZE; + template + using MaskType = MaskT; + template +@@ -4270,31 +5062,49 @@ + using MaskIterT = typename Mask::template Iterator; + + /// @brief Visits all active values in a leaf node +- class ValueOnIterator : public MaskIterT ++ class ValueOnIterator ++#if !defined (__KERNEL_METAL__) ++ : public MaskIterT ++#endif + { ++#if defined(__KERNEL_METAL__) ++ MaskIterT BaseT; ++#define BASE(v) BaseT.v ++#else + using BaseT = MaskIterT; +- const LeafNode *mParent; ++#define BASE(v) BaseT::v ++#endif ++ __global__ const LeafNode *mParent; + public: + __hostdev__ ValueOnIterator() : BaseT(), mParent(nullptr) {} +- __hostdev__ ValueOnIterator(const LeafNode* parent) : BaseT(parent->data()->mValueMask.beginOn()), mParent(parent) {} +- ValueOnIterator& operator=(const ValueOnIterator&) = default; +- __hostdev__ ValueType operator*() const {NANOVDB_ASSERT(*this); return mParent->getValue(BaseT::pos());} +- __hostdev__ CoordT getCoord() const { NANOVDB_ASSERT(*this); return mParent->offsetToGlobalCoord(BaseT::pos());} ++ __hostdev__ ValueOnIterator(__global__ const LeafNode* parent) : BaseT(parent->data()->mValueMask.beginOn()), mParent(parent) {} ++ __global__ ValueOnIterator& operator=(__global__ const ValueOnIterator&) = default; ++ __hostdev__ ValueType operator*() const {NANOVDB_ASSERT(*this); return mParent->getValue(BASE(pos)());} ++ __hostdev__ CoordT getCoord() const { NANOVDB_ASSERT(*this); return mParent->offsetToGlobalCoord(BASE(pos)());} + }; // Member class ValueOnIterator + + ValueOnIterator beginValueOn() const {return ValueOnIterator(this);} + + /// @brief Visits all inactive values in a leaf node +- class ValueOffIterator : public MaskIterT ++ class ValueOffIterator ++#if !defined (__KERNEL_METAL__) ++ : public MaskIterT ++#endif + { ++#if defined(__KERNEL_METAL__) ++ MaskIterT BaseT; ++#define BASE(v) BaseT.v ++#else + using BaseT = MaskIterT; +- const LeafNode *mParent; ++#define BASE(v) BaseT::v ++#endif ++ __global__ const LeafNode *mParent; + public: + __hostdev__ ValueOffIterator() : BaseT(), mParent(nullptr) {} +- __hostdev__ ValueOffIterator(const LeafNode* parent) : BaseT(parent->data()->mValueMask.beginOff()), mParent(parent) {} +- ValueOffIterator& operator=(const ValueOffIterator&) = default; +- __hostdev__ ValueType operator*() const {NANOVDB_ASSERT(*this); return mParent->getValue(BaseT::pos());} +- __hostdev__ CoordT getCoord() const { NANOVDB_ASSERT(*this); return mParent->offsetToGlobalCoord(BaseT::pos());} ++ __hostdev__ ValueOffIterator(__global__ const LeafNode* parent) : BaseT(parent->data()->mValueMask.beginOff()), mParent(parent) {} ++ __global__ ValueOffIterator& operator=(__global__ const ValueOffIterator&) = default; ++ __hostdev__ ValueType operator*() const {NANOVDB_ASSERT(*this); return mParent->getValue(BASE(pos)());} ++ __hostdev__ CoordT getCoord() const { NANOVDB_ASSERT(*this); return mParent->offsetToGlobalCoord(BASE(pos)());} + }; // Member class ValueOffIterator + + ValueOffIterator beginValueOff() const {return ValueOffIterator(this);} +@@ -4302,17 +5112,17 @@ + /// @brief Visits all values in a leaf node, i.e. both active and inactive values + class ValueIterator + { +- const LeafNode *mParent; ++ __global__ const LeafNode *mParent; + uint32_t mPos; + public: + __hostdev__ ValueIterator() : mParent(nullptr), mPos(1u << 3 * Log2Dim) {} +- __hostdev__ ValueIterator(const LeafNode* parent) : mParent(parent), mPos(0) {NANOVDB_ASSERT(parent);} +- ValueIterator& operator=(const ValueIterator&) = default; ++ __hostdev__ ValueIterator(__global__ const LeafNode* parent) : mParent(parent), mPos(0) {NANOVDB_ASSERT(parent);} ++ __global__ ValueIterator& operator=(__global__ const ValueIterator&) = default; + __hostdev__ ValueType operator*() const { NANOVDB_ASSERT(*this); return mParent->getValue(mPos);} + __hostdev__ CoordT getCoord() const { NANOVDB_ASSERT(*this); return mParent->offsetToGlobalCoord(mPos);} + __hostdev__ bool isActive() const { NANOVDB_ASSERT(*this); return mParent->isActive(mPos);} + __hostdev__ operator bool() const {return mPos < (1u << 3 * Log2Dim);} +- __hostdev__ ValueIterator& operator++() {++mPos; return *this;} ++ __hostdev__ __global__ ValueIterator& operator++() {++mPos; return *this;} + __hostdev__ ValueIterator operator++(int) { + auto tmp = *this; + ++(*this); +@@ -4320,43 +5130,49 @@ + } + }; // Member class ValueIterator + ++#if defined(__KERNEL_METAL__) ++#define BASE(v) _base.v ++#else ++#define BASE(v) DataType::v ++#endif ++ + ValueIterator beginValue() const {return ValueIterator(this);} + + static_assert(is_same::Type>::value, "Mismatching BuildType"); +- static constexpr uint32_t LOG2DIM = Log2Dim; +- static constexpr uint32_t TOTAL = LOG2DIM; // needed by parent nodes +- static constexpr uint32_t DIM = 1u << TOTAL; // number of voxels along each axis of this node +- static constexpr uint32_t SIZE = 1u << 3 * LOG2DIM; // total number of voxels represented by this node +- static constexpr uint32_t MASK = (1u << LOG2DIM) - 1u; // mask for bit operations +- static constexpr uint32_t LEVEL = 0; // level 0 = leaf +- static constexpr uint64_t NUM_VALUES = uint64_t(1) << (3 * TOTAL); // total voxel count represented by this node ++ static __constant__ constexpr uint32_t LOG2DIM = Log2Dim; ++ static __constant__ constexpr uint32_t TOTAL = LOG2DIM; // needed by parent nodes ++ static __constant__ constexpr uint32_t DIM = 1u << TOTAL; // number of voxels along each axis of this node ++ static __constant__ constexpr uint32_t SIZE = 1u << 3 * LOG2DIM; // total number of voxels represented by this node ++ static __constant__ constexpr uint32_t MASK = (1u << LOG2DIM) - 1u; // mask for bit operations ++ static __constant__ constexpr uint32_t LEVEL = 0; // level 0 = leaf ++ static __constant__ constexpr uint64_t NUM_VALUES = uint64_t(1) << (3 * TOTAL); // total voxel count represented by this node + +- __hostdev__ DataType* data() { return reinterpret_cast(this); } ++ __hostdev__ __global__ DataType* data() __global__ { return reinterpret_cast<__global__ DataType*>(this); } + +- __hostdev__ const DataType* data() const { return reinterpret_cast(this); } ++ __hostdev__ __global__ const DataType* data() __global__ const { return reinterpret_cast<__global__ const DataType*>(this); } + + /// @brief Return a const reference to the bit mask of active voxels in this leaf node +- __hostdev__ const MaskType& valueMask() const { return DataType::mValueMask; } ++ __hostdev__ __global__ const MaskType& valueMask() const __global__ { return DataType::mValueMask; } + + /// @brief Return a const reference to the minimum active value encoded in this leaf node +- __hostdev__ ValueType minimum() const { return this->getMin(); } ++ __hostdev__ ValueType minimum() const __global__ { return this->getMin(); } + + /// @brief Return a const reference to the maximum active value encoded in this leaf node +- __hostdev__ ValueType maximum() const { return this->getMax(); } ++ __hostdev__ ValueType maximum() const __global__ { return this->getMax(); } + + /// @brief Return a const reference to the average of all the active values encoded in this leaf node +- __hostdev__ FloatType average() const { return DataType::getAvg(); } ++ __hostdev__ FloatType average() const __global__ { return DataType::getAvg(); } + + /// @brief Return the variance of all the active values encoded in this leaf node +- __hostdev__ FloatType variance() const { return DataType::getDev()*DataType::getDev(); } ++ __hostdev__ FloatType variance() const __global__ { return DataType::getDev()*DataType::getDev(); } + + /// @brief Return a const reference to the standard deviation of all the active values encoded in this leaf node +- __hostdev__ FloatType stdDeviation() const { return DataType::getDev(); } ++ __hostdev__ FloatType stdDeviation() const __global__ { return DataType::getDev(); } + +- __hostdev__ uint8_t flags() const { return DataType::mFlags; } ++ __hostdev__ uint8_t flags() const __global__ { return DataType::mFlags; } + + /// @brief Return the origin in index space of this leaf node +- __hostdev__ CoordT origin() const { return DataType::mBBoxMin & ~MASK; } ++ __hostdev__ CoordT origin() const __global__ { return DataType::mBBoxMin & ~MASK; } + + __hostdev__ static CoordT OffsetToLocalCoord(uint32_t n) + { +@@ -4366,9 +5182,9 @@ + } + + /// @brief Converts (in place) a local index coordinate to a global index coordinate +- __hostdev__ void localToGlobalCoord(Coord& ijk) const { ijk += this->origin(); } ++ __hostdev__ void localToGlobalCoord(__global__ Coord& ijk) const __global__ { ijk += this->origin(); } + +- __hostdev__ CoordT offsetToGlobalCoord(uint32_t n) const ++ __hostdev__ CoordT offsetToGlobalCoord(uint32_t n) const __global__ + { + return OffsetToLocalCoord(n) + this->origin(); + } +@@ -4377,7 +5193,7 @@ + __hostdev__ static uint32_t dim() { return 1u << LOG2DIM; } + + /// @brief Return the bounding box in index space of active values in this leaf node +- __hostdev__ BBox bbox() const ++ __hostdev__ BBox bbox() const __global__ + { + BBox bbox(DataType::mBBoxMin, DataType::mBBoxMin); + if ( this->hasBBox() ) { +@@ -4399,54 +5215,85 @@ + __hostdev__ uint64_t memUsage() { return DataType::memUsage(); } + + /// @brief This class cannot be constructed or deleted +- LeafNode() = delete; +- LeafNode(const LeafNode&) = delete; +- LeafNode& operator=(const LeafNode&) = delete; +- ~LeafNode() = delete; ++ LeafNode() __global__ = delete; ++ LeafNode(__global__ const LeafNode&) __global__ = delete; ++ __global__ LeafNode& operator=(__global__ const LeafNode&) __global__ = delete; ++ ~LeafNode() __global__ = delete; + + /// @brief Return the voxel value at the given offset. +- __hostdev__ ValueType getValue(uint32_t offset) const { return DataType::getValue(offset); } + ++ __hostdev__ ValueType getValue(uint32_t offset) const __global__ { return DataType::getValue(offset); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ ValueType getValue(uint32_t offset) const __local__ { return DataType::getValue(offset); } ++#endif ++ + /// @brief Return the voxel value at the given coordinate. +- __hostdev__ ValueType getValue(const CoordT& ijk) const { return DataType::getValue(CoordToOffset(ijk)); } ++ __hostdev__ ValueType getValue(__global__ const CoordT& ijk) const __global__ { return BASE(getValue)(CoordToOffset(ijk)); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ ValueType getValue(__local__ const CoordT& ijk) const __global__ { return BASE(getValue)(CoordToOffset(ijk)); } ++ __hostdev__ ValueType getValue(__local__ const CoordT& ijk) const __local__ { return BASE(getValue)(CoordToOffset(ijk)); } ++#endif + + /// @brief Sets the value at the specified location and activate its state. + /// + /// @note This is safe since it does not change the topology of the tree (unlike setValue methods on the other nodes) +- __hostdev__ void setValue(const CoordT& ijk, const ValueType& v) { DataType::setValue(CoordToOffset(ijk), v); } ++ __hostdev__ void setValue(__global__ const CoordT& ijk, __global__ const ValueType& v) __global__ { DataType::setValue(CoordToOffset(ijk), v); } + + /// @brief Sets the value at the specified location but leaves its state unchanged. + /// + /// @note This is safe since it does not change the topology of the tree (unlike setValue methods on the other nodes) +- __hostdev__ void setValueOnly(uint32_t offset, const ValueType& v) { DataType::setValueOnly(offset, v); } +- __hostdev__ void setValueOnly(const CoordT& ijk, const ValueType& v) { DataType::setValueOnly(CoordToOffset(ijk), v); } ++ __hostdev__ void setValueOnly(uint32_t offset, __global__ const ValueType& v) __global__ { DataType::setValueOnly(offset, v); } ++ __hostdev__ void setValueOnly(__global__ const CoordT& ijk, __global__ const ValueType& v) __global__ { DataType::setValueOnly(CoordToOffset(ijk), v); } + + /// @brief Return @c true if the voxel value at the given coordinate is active. +- __hostdev__ bool isActive(const CoordT& ijk) const { return DataType::mValueMask.isOn(CoordToOffset(ijk)); } +- __hostdev__ bool isActive(uint32_t n) const { return DataType::mValueMask.isOn(n); } ++ __hostdev__ bool isActive(__global__ const CoordT& ijk) const __global__ { return BASE(mValueMask).isOn(CoordToOffset(ijk)); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isActive(__local__ const CoordT& ijk) const __global__ { return BASE(mValueMask).isOn(CoordToOffset(ijk)); } ++ __hostdev__ bool isActive(__local__ const CoordT& ijk) const __local__ { return BASE(mValueMask).isOn(CoordToOffset(ijk)); } ++#endif + ++ __hostdev__ bool isActive(uint32_t n) const __global__ { return BASE(mValueMask).isOn(n); } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isActive(uint32_t n) const __local__ { return BASE(mValueMask).isOn(n); } ++#endif ++ + /// @brief Return @c true if any of the voxel value are active in this leaf node. +- __hostdev__ bool isActive() const ++ __hostdev__ bool isActive() const __global__ + { + //NANOVDB_ASSERT( bool(DataType::mFlags & uint8_t(2)) != DataType::mValueMask.isOff() ); + //return DataType::mFlags & uint8_t(2); + return !DataType::mValueMask.isOff(); + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isActive() const __local__ ++ { ++ NANOVDB_ASSERT( bool(DataType::mFlags & uint8_t(2)) != BASE(mValueMask).isOff() ); ++ return DataType::mFlags & uint8_t(2); ++ } ++#endif + + __hostdev__ bool hasBBox() const {return DataType::mFlags & uint8_t(2);} + + /// @brief Return @c true if the voxel value at the given coordinate is active and updates @c v with the value. +- __hostdev__ bool probeValue(const CoordT& ijk, ValueType& v) const ++ __hostdev__ bool probeValue(__global__ const CoordT& ijk, __global__ ValueType& v) const __global__ + { + const uint32_t n = CoordToOffset(ijk); + v = DataType::getValue(n); +- return DataType::mValueMask.isOn(n); ++ return BASE(mValueMask).isOn(n); + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool probeValue(__local__ const CoordT& ijk, __local__ ValueType& v) const __global__ ++ { ++ const uint32_t n = CoordToOffset(ijk); ++ v = BASE(getValue)(n); ++ return BASE(mValueMask).isOn(n); ++ } ++#endif + +- __hostdev__ const LeafNode* probeLeaf(const CoordT&) const { return this; } ++ __hostdev__ __global__ const LeafNode* probeLeaf(__global__ const CoordT&) const __global__ { return this; } + + /// @brief Return the linear offset corresponding to the given coordinate +- __hostdev__ static uint32_t CoordToOffset(const CoordT& ijk) ++ __hostdev__ static uint32_t CoordToOffset(__global__ const CoordT& ijk) + { + #if 0 + return ((ijk[0] & MASK) << (2 * LOG2DIM)) + ((ijk[1] & MASK) << LOG2DIM) + (ijk[2] & MASK); +@@ -4454,6 +5301,16 @@ + return ((ijk[0] & MASK) << (2 * LOG2DIM)) | ((ijk[1] & MASK) << LOG2DIM) | (ijk[2] & MASK); + #endif + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ static uint32_t CoordToOffset(__local__ const CoordT& ijk) ++ { ++ #if 0 ++ return ((ijk[0] & MASK) << (2 * LOG2DIM)) + ((ijk[1] & MASK) << LOG2DIM) + (ijk[2] & MASK); ++ #else ++ return ((ijk[0] & MASK) << (2 * LOG2DIM)) | ((ijk[1] & MASK) << LOG2DIM) | (ijk[2] & MASK); ++ #endif ++ } ++#endif + + /// @brief Updates the local bounding box of active voxels in this node. Return true if bbox was updated. + /// +@@ -4461,8 +5318,9 @@ + /// + /// @details This method is based on few (intrinsic) bit operations and hence is relatively fast. + /// However, it should only only be called of either the value mask has changed or if the ++ + /// active bounding box is still undefined. e.g. during construction of this node. +- __hostdev__ bool updateBBox(); ++ __hostdev__ bool updateBBox() __global__; + + private: + static_assert(sizeof(DataType) % NANOVDB_DATA_ALIGNMENT == 0, "sizeof(LeafData) is misaligned"); +@@ -4478,11 +5336,15 @@ + + /// @brief Private method to return a voxel value and update a (dummy) ReadAccessor + template +- __hostdev__ ValueType getValueAndCache(const CoordT& ijk, const AccT&) const { return this->getValue(ijk); } ++ __hostdev__ ValueType getValueAndCache(__global__ const CoordT& ijk, __global__ const AccT&) const __global__ { return this->getValue(ijk); } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ ValueType getValueAndCache(__local__ const CoordT& ijk, __local__ const AccT&) const __global__ { return this->getValue(ijk); } ++#endif + + /// @brief Return the node information. + template +- __hostdev__ typename AccT::NodeInfo getNodeInfoAndCache(const CoordType& /*ijk*/, const AccT& /*acc*/) const { ++ __hostdev__ typename AccT::NodeInfo getNodeInfoAndCache(__global__ const CoordType& /*ijk*/, __global__ const AccT& /*acc*/) const __global__ { + using NodeInfoT = typename AccT::NodeInfo; + return NodeInfoT{LEVEL, this->dim(), this->minimum(), this->maximum(), + this->average(), this->stdDeviation(), this->bbox()[0], this->bbox()[1]}; +@@ -4489,16 +5351,20 @@ + } + + template +- __hostdev__ bool isActiveAndCache(const CoordT& ijk, const AccT&) const { return this->isActive(ijk); } ++ __hostdev__ bool isActiveAndCache(__global__ const CoordT& ijk, __global__ const AccT&) const __global__ { return this->isActive(ijk); } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ bool isActiveAndCache(__local__ const CoordT& ijk, __local__ const AccT&) const __global__ { return this->isActive(ijk); } ++#endif + + template +- __hostdev__ bool probeValueAndCache(const CoordT& ijk, ValueType& v, const AccT&) const { return this->probeValue(ijk, v); } ++ __hostdev__ bool probeValueAndCache(__global__ const CoordT& ijk, __global__ ValueType& v, __global__ const AccT&) const __global__ { return this->probeValue(ijk, v); } + + template +- __hostdev__ const LeafNode* probeLeafAndCache(const CoordT&, const AccT&) const { return this; } ++ __hostdev__ __global__ const LeafNode* probeLeafAndCache(__global__ const CoordT&, __global__ const AccT&) const __global__ { return this; } + + template +- __hostdev__ uint32_t getDimAndCache(const CoordT&, const RayT& /*ray*/, const AccT&) const ++ __hostdev__ uint32_t getDimAndCache(__global__ const CoordT&, __global__ const RayT& /*ray*/, __global__ const AccT&) const __global__ + { + if (DataType::mFlags & uint8_t(1u)) return this->dim(); // skip this node if the 1st bit is set + +@@ -4505,11 +5371,21 @@ + //if (!ray.intersects( this->bbox() )) return 1 << LOG2DIM; + return ChildNodeType::dim(); + } +- ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ uint32_t getDimAndCache(__local__ const CoordT&, __local__ const RayT& /*ray*/, __local__ const AccT&) const __global__ ++ { ++ if (BASE(mFlags) & uint8_t(1)) ++ return this->dim(); // skip this node if first bit is set ++ //if (!ray.intersects( this->bbox() )) return 1 << LOG2DIM; ++ return ChildNodeType::dim(); ++ } ++#endif ++#undef BASE + }; // LeafNode class + + template class MaskT, uint32_t LOG2DIM> +-__hostdev__ inline bool LeafNode::updateBBox() ++__hostdev__ inline bool LeafNode::updateBBox() __global__ + { + static_assert(LOG2DIM == 3, "LeafNode::updateBBox: only supports LOGDIM = 3!"); + if (DataType::mValueMask.isOff()) { +@@ -4516,11 +5392,21 @@ + DataType::mFlags &= ~uint8_t(2);// set 2nd bit off, which indicates that this nodes has no bbox + return false; + } ++#if defined(__KERNEL_METAL__) ++ struct Update { ++ static void update(__global__ DataType &d, uint32_t min, uint32_t max, int axis) { ++ NANOVDB_ASSERT(min <= max && max < 8); ++ d.mBBoxMin[axis] = (d.mBBoxMin[axis] & ~MASK) + int(min); ++ d.mBBoxDif[axis] = uint8_t(max - min); ++ } ++ }; ++#else + auto update = [&](uint32_t min, uint32_t max, int axis) { + NANOVDB_ASSERT(min <= max && max < 8); + DataType::mBBoxMin[axis] = (DataType::mBBoxMin[axis] & ~MASK) + int(min); + DataType::mBBoxDif[axis] = uint8_t(max - min); + }; ++#endif + uint64_t word64 = DataType::mValueMask.template getWord(0); + uint32_t Xmin = word64 ? 0u : 8u; + uint32_t Xmax = Xmin; +@@ -4534,6 +5420,17 @@ + } + } + NANOVDB_ASSERT(word64); ++#if defined(__KERNEL_METAL__) ++ Update::update(this, Xmin, Xmax, 0); ++ Update::update(this, FindLowestOn(word64) >> 3, FindHighestOn(word64) >> 3, 1); ++ __local__ const uint32_t *p = reinterpret_cast<__local__ const uint32_t*>(&word64), word32 = p[0] | p[1]; ++ __local__ const uint16_t *q = reinterpret_cast<__local__ const uint16_t*>(&word32), word16 = q[0] | q[1]; ++ __local__ const uint8_t *b = reinterpret_cast<__local__ const uint8_t* >(&word16), byte = b[0] | b[1]; ++ NANOVDB_ASSERT(byte); ++ Update::update(this, FindLowestOn(static_cast(byte)), FindHighestOn(static_cast(byte)), 2); ++ DataType::mFlags |= uint8_t(2);// set 2nd bit on, which indicates that this nodes has a bbox ++ return true; ++#else + update(Xmin, Xmax, 0); + update(FindLowestOn(word64) >> 3, FindHighestOn(word64) >> 3, 1); + const uint32_t *p = reinterpret_cast(&word64), word32 = p[0] | p[1]; +@@ -4541,8 +5438,9 @@ + const uint8_t *b = reinterpret_cast(&word16), byte = b[0] | b[1]; + NANOVDB_ASSERT(byte); + update(FindLowestOn(static_cast(byte)), FindHighestOn(static_cast(byte)), 2); +- DataType::mFlags |= uint8_t(2);// set 2nd bit on, which indicates that this nodes has a bbox ++ DataType::mFlags |= uint8_t(2);// set 2nd bit on, which indicates that this nodes has a bbox + return true; ++#endif + } // LeafNode::updateBBox + + // --------------------------> Template specializations and traits <------------------------------------ +@@ -4651,12 +5549,12 @@ + using FloatType = typename RootT::FloatType; + using CoordValueType = typename RootT::CoordType::ValueType; + +- mutable const RootT* mRoot; // 8 bytes (mutable to allow for access methods to be const) ++ mutable __global__ const RootT* mRoot; // 8 bytes (mutable to allow for access methods to be const) + public: + using ValueType = typename RootT::ValueType; + using CoordType = typename RootT::CoordType; + +- static const int CacheLevels = 0; ++ static __constant__ const int CacheLevels = 0; + + struct NodeInfo { + uint32_t mLevel; // 4B +@@ -4670,60 +5568,77 @@ + }; + + /// @brief Constructor from a root node +- __hostdev__ ReadAccessor(const RootT& root) : mRoot{&root} {} ++ __hostdev__ ReadAccessor(__global__ const RootT& root) __local__ : mRoot{&root} {} + + /// @brief Constructor from a grid +- __hostdev__ ReadAccessor(const GridT& grid) : ReadAccessor(grid.tree().root()) {} ++ __hostdev__ ReadAccessor(__global__ const GridT& grid) __local__ : ReadAccessor(grid.tree().root()) {} + + /// @brief Constructor from a tree +- __hostdev__ ReadAccessor(const TreeT& tree) : ReadAccessor(tree.root()) {} ++ __hostdev__ ReadAccessor(__global__ const TreeT& tree) __local__ : ReadAccessor(tree.root()) {} + + /// @brief Reset this access to its initial state, i.e. with an empty cache + /// @node Noop since this template specialization has no cache + __hostdev__ void clear() {} + +- __hostdev__ const RootT& root() const { return *mRoot; } ++ __hostdev__ __global__ const RootT& root() const __global__ { return *mRoot; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __global__ const RootT& root() const __local__ { return *mRoot; } ++#endif + + /// @brief Defaults constructors +- ReadAccessor(const ReadAccessor&) = default; +- ~ReadAccessor() = default; +- ReadAccessor& operator=(const ReadAccessor&) = default; ++ ReadAccessor(__local__ const ReadAccessor&) __local__ = default; ++ ~ReadAccessor() __local__ = default; ++ __local__ ReadAccessor& operator=(__local__ const ReadAccessor&) __local__ = default; + +- __hostdev__ ValueType getValue(const CoordType& ijk) const ++ __hostdev__ ValueType getValue(__global__ const CoordType& ijk) const __local__ + { + return mRoot->getValueAndCache(ijk, *this); + } +- __hostdev__ ValueType operator()(const CoordType& ijk) const ++ ++#if defined(__KERNEL_METAL__) ++ __hostdev__ ValueType getValue(__local__ const CoordType& ijk) const __local__ + { ++ return mRoot->getValueAndCache(ijk, *this); ++ } ++#endif ++ ++ __hostdev__ ValueType operator()(__global__ const CoordType& ijk) const __local__ ++ { + return this->getValue(ijk); + } +- __hostdev__ ValueType operator()(int i, int j, int k) const ++ __hostdev__ ValueType operator()(int i, int j, int k) const __local__ + { + return this->getValue(CoordType(i,j,k)); + } + +- __hostdev__ NodeInfo getNodeInfo(const CoordType& ijk) const ++ __hostdev__ NodeInfo getNodeInfo(__global__ const CoordType& ijk) const __local__ + { + return mRoot->getNodeInfoAndCache(ijk, *this); + } + +- __hostdev__ bool isActive(const CoordType& ijk) const ++ __hostdev__ bool isActive(__global__ const CoordType& ijk) const __local__ + { + return mRoot->isActiveAndCache(ijk, *this); + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isActive(__local__ const CoordType& ijk) const __local__ ++ { ++ return mRoot->isActiveAndCache(ijk, *this); ++ } ++#endif + +- __hostdev__ bool probeValue(const CoordType& ijk, ValueType& v) const ++ __hostdev__ bool probeValue(__global__ const CoordType& ijk, __global__ ValueType& v) const __local__ + { + return mRoot->probeValueAndCache(ijk, v, *this); + } + +- __hostdev__ const LeafT* probeLeaf(const CoordType& ijk) const ++ __hostdev__ __global__ const LeafT* probeLeaf(__global__ const CoordType& ijk) const __local__ + { + return mRoot->probeLeafAndCache(ijk, *this); + } + + template +- __hostdev__ uint32_t getDim(const CoordType& ijk, const RayT& ray) const ++ __hostdev__ uint32_t getDim(__global__ const CoordType& ijk, __global__ const RayT& ray) const __local__ + { + return mRoot->getDimAndCache(ijk, ray, *this); + } +@@ -4739,7 +5654,11 @@ + + /// @brief No-op + template +- __hostdev__ void insert(const CoordType&, const NodeT*) const {} ++ __hostdev__ void insert(__global__ const CoordType&, __global__ const NodeT*) const __local__ {} ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ void insert(__local__ const CoordType&, __global__ const NodeT*) const __local__ {} ++#endif + }; // ReadAccessor class + + /// @brief Node caching at a single tree level +@@ -4761,19 +5680,19 @@ + + // All member data are mutable to allow for access methods to be const + mutable CoordT mKey; // 3*4 = 12 bytes +- mutable const RootT* mRoot; // 8 bytes +- mutable const NodeT* mNode; // 8 bytes ++ mutable __global__ const RootT* mRoot; // 8 bytes ++ mutable __global__ const NodeT* mNode; // 8 bytes + + public: + using ValueType = ValueT; + using CoordType = CoordT; + +- static const int CacheLevels = 1; ++ static __constant__ const int CacheLevels = 1; + + using NodeInfo = typename ReadAccessor::NodeInfo; + + /// @brief Constructor from a root node +- __hostdev__ ReadAccessor(const RootT& root) ++ __hostdev__ ReadAccessor(__global__ const RootT& root) __local__ + : mKey(CoordType::max()) + , mRoot(&root) + , mNode(nullptr) +@@ -4781,10 +5700,10 @@ + } + + /// @brief Constructor from a grid +- __hostdev__ ReadAccessor(const GridT& grid) : ReadAccessor(grid.tree().root()) {} ++ __hostdev__ ReadAccessor(__global__ const GridT& grid) __local__ : ReadAccessor(grid.tree().root()) {} + + /// @brief Constructor from a tree +- __hostdev__ ReadAccessor(const TreeT& tree) : ReadAccessor(tree.root()) {} ++ __hostdev__ ReadAccessor(__global__ const TreeT& tree) __local__ : ReadAccessor(tree.root()) {} + + /// @brief Reset this access to its initial state, i.e. with an empty cache + __hostdev__ void clear() +@@ -4793,21 +5712,38 @@ + mNode = nullptr; + } + +- __hostdev__ const RootT& root() const { return *mRoot; } ++ __hostdev__ __global__ const RootT& root() const __global__ { return *mRoot; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __global__ const RootT& root() const __local__ { return *mRoot; } ++#endif + + /// @brief Defaults constructors +- ReadAccessor(const ReadAccessor&) = default; +- ~ReadAccessor() = default; +- ReadAccessor& operator=(const ReadAccessor&) = default; ++ ReadAccessor(__global__ const ReadAccessor&) __global__ = default; ++ ~ReadAccessor() __global__ = default; ++ __global__ ReadAccessor& operator=(__global__ const ReadAccessor&) __global__ = default; + +- __hostdev__ bool isCached(const CoordType& ijk) const ++ __hostdev__ bool isCached(__global__ const CoordType& ijk) const __global__ + { + return (ijk[0] & int32_t(~NodeT::MASK)) == mKey[0] && + (ijk[1] & int32_t(~NodeT::MASK)) == mKey[1] && + (ijk[2] & int32_t(~NodeT::MASK)) == mKey[2]; + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isCached(__local__ const CoordType& ijk) const __global__ ++ { ++ return (ijk[0] & int32_t(~NodeT::MASK)) == mKey[0] && ++ (ijk[1] & int32_t(~NodeT::MASK)) == mKey[1] && ++ (ijk[2] & int32_t(~NodeT::MASK)) == mKey[2]; ++ } ++ __hostdev__ bool isCached(__local__ const CoordType& ijk) const __local__ ++ { ++ return (ijk[0] & int32_t(~NodeT::MASK)) == mKey[0] && ++ (ijk[1] & int32_t(~NodeT::MASK)) == mKey[1] && ++ (ijk[2] & int32_t(~NodeT::MASK)) == mKey[2]; ++ } ++#endif + +- __hostdev__ ValueType getValue(const CoordType& ijk) const ++ __hostdev__ ValueType getValue(__global__ const CoordType& ijk) const __global__ + { + if (this->isCached(ijk)) { + return mNode->getValueAndCache(ijk, *this); +@@ -4814,16 +5750,26 @@ + } + return mRoot->getValueAndCache(ijk, *this); + } +- __hostdev__ ValueType operator()(const CoordType& ijk) const ++#if defined(__KERNEL_METAL__) ++ __hostdev__ ValueType getValue(__local__ const CoordType& ijk) const __global__ + { ++ if (this->isCached(ijk)) { ++ return mNode->getValueAndCache(ijk, *this); ++ } ++ return mRoot->getValueAndCache(ijk, *this); ++ } ++#endif ++ ++ __hostdev__ ValueType operator()(__global__ const CoordType& ijk) const __global__ ++ { + return this->getValue(ijk); + } +- __hostdev__ ValueType operator()(int i, int j, int k) const ++ __hostdev__ ValueType operator()(int i, int j, int k) const __global__ + { + return this->getValue(CoordType(i,j,k)); + } + +- __hostdev__ NodeInfo getNodeInfo(const CoordType& ijk) const ++ __hostdev__ NodeInfo getNodeInfo(__global__ const CoordType& ijk) const __global__ + { + if (this->isCached(ijk)) { + return mNode->getNodeInfoAndCache(ijk, *this); +@@ -4831,7 +5777,7 @@ + return mRoot->getNodeInfoAndCache(ijk, *this); + } + +- __hostdev__ bool isActive(const CoordType& ijk) const ++ __hostdev__ bool isActive(__global__ const CoordType& ijk) const __global__ + { + if (this->isCached(ijk)) { + return mNode->isActiveAndCache(ijk, *this); +@@ -4838,8 +5784,17 @@ + } + return mRoot->isActiveAndCache(ijk, *this); + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isActive(__global__ const CoordType& ijk) const __local__ ++ { ++ if (this->isCached(ijk)) { ++ return mNode->isActiveAndCache(ijk, *this); ++ } ++ return mRoot->isActiveAndCache(ijk, *this); ++ } ++#endif + +- __hostdev__ bool probeValue(const CoordType& ijk, ValueType& v) const ++ __hostdev__ bool probeValue(__global__ const CoordType& ijk, __global__ ValueType& v) const __global__ + { + if (this->isCached(ijk)) { + return mNode->probeValueAndCache(ijk, v, *this); +@@ -4847,7 +5802,7 @@ + return mRoot->probeValueAndCache(ijk, v, *this); + } + +- __hostdev__ const LeafT* probeLeaf(const CoordType& ijk) const ++ __hostdev__ __global__ const LeafT* probeLeaf(__global__ const CoordType& ijk) const __global__ + { + if (this->isCached(ijk)) { + return mNode->probeLeafAndCache(ijk, *this); +@@ -4856,7 +5811,7 @@ + } + + template +- __hostdev__ uint32_t getDim(const CoordType& ijk, const RayT& ray) const ++ __hostdev__ uint32_t getDim(__global__ const CoordType& ijk, __global__ const RayT& ray) const __global__ + { + if (this->isCached(ijk)) { + return mNode->getDimAndCache(ijk, ray, *this); +@@ -4874,15 +5829,26 @@ + friend class LeafNode; + + /// @brief Inserts a leaf node and key pair into this ReadAccessor +- __hostdev__ void insert(const CoordType& ijk, const NodeT* node) const ++ __hostdev__ void insert(__global__ const CoordType& ijk, __global__ const NodeT* node) const __local__ + { + mKey = ijk & ~NodeT::MASK; + mNode = node; + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ void insert(__local__ const CoordType& ijk, __global__ const NodeT* node) const __local__ ++ { ++ mKey = ijk & ~NodeT::MASK; ++ mNode = node; ++ } ++#endif + + // no-op + template +- __hostdev__ void insert(const CoordType&, const OtherNodeT*) const {} ++ __hostdev__ void insert(__global__ const CoordType&, __global__ const OtherNodeT*) const __local__ {} ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ void insert(__local__ const CoordType&, __global__ const OtherNodeT*) const __local__ {} ++#endif + + }; // ReadAccessor + +@@ -4909,20 +5875,20 @@ + #else // 68 bytes total + mutable CoordT mKeys[2]; // 2*3*4 = 24 bytes + #endif +- mutable const RootT* mRoot; +- mutable const Node1T* mNode1; +- mutable const Node2T* mNode2; ++ mutable __global__ const RootT* mRoot; ++ mutable __global__ const Node1T* mNode1; ++ mutable __global__ const Node2T* mNode2; + + public: + using ValueType = ValueT; + using CoordType = CoordT; + +- static const int CacheLevels = 2; ++ static __constant__ const int CacheLevels = 2; + + using NodeInfo = typename ReadAccessor::NodeInfo; + + /// @brief Constructor from a root node +- __hostdev__ ReadAccessor(const RootT& root) ++ __hostdev__ ReadAccessor(__global__ const RootT& root) __local__ + #ifdef USE_SINGLE_ACCESSOR_KEY + : mKey(CoordType::max()) + #else +@@ -4935,10 +5901,10 @@ + } + + /// @brief Constructor from a grid +- __hostdev__ ReadAccessor(const GridT& grid) : ReadAccessor(grid.tree().root()) {} ++ __hostdev__ ReadAccessor(__global__ const GridT& grid) __local__ : ReadAccessor(grid.tree().root()) {} + + /// @brief Constructor from a tree +- __hostdev__ ReadAccessor(const TreeT& tree) : ReadAccessor(tree.root()) {} ++ __hostdev__ ReadAccessor(__global__ const TreeT& tree) __local__ : ReadAccessor(tree.root()) {} + + /// @brief Reset this access to its initial state, i.e. with an empty cache + __hostdev__ void clear() +@@ -4952,15 +5918,18 @@ + mNode2 = nullptr; + } + +- __hostdev__ const RootT& root() const { return *mRoot; } ++ __hostdev__ __global__ const RootT& root() const __global__ { return *mRoot; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __global__ const RootT& root() const __local__ { return *mRoot; } ++#endif + + /// @brief Defaults constructors +- ReadAccessor(const ReadAccessor&) = default; ++ ReadAccessor(__global__ const ReadAccessor&) __global__ = default; + ~ReadAccessor() = default; +- ReadAccessor& operator=(const ReadAccessor&) = default; ++ __global__ ReadAccessor& operator=(__global__ const ReadAccessor&) __global__ = default; + + #ifdef USE_SINGLE_ACCESSOR_KEY +- __hostdev__ bool isCached1(CoordValueType dirty) const ++ __hostdev__ bool isCached1(CoordValueType dirty) const __global__ + { + if (!mNode1) + return false; +@@ -4970,7 +5939,7 @@ + } + return true; + } +- __hostdev__ bool isCached2(CoordValueType dirty) const ++ __hostdev__ bool isCached2(CoordValueType dirty) const __global__ + { + if (!mNode2) + return false; +@@ -4980,18 +5949,18 @@ + } + return true; + } +- __hostdev__ CoordValueType computeDirty(const CoordType& ijk) const ++ __hostdev__ CoordValueType computeDirty(__global__ const CoordType& ijk) const __global__ + { + return (ijk[0] ^ mKey[0]) | (ijk[1] ^ mKey[1]) | (ijk[2] ^ mKey[2]); + } + #else +- __hostdev__ bool isCached1(const CoordType& ijk) const ++ __hostdev__ bool isCached1(__global__ const CoordType& ijk) const __global__ + { + return (ijk[0] & int32_t(~Node1T::MASK)) == mKeys[0][0] && + (ijk[1] & int32_t(~Node1T::MASK)) == mKeys[0][1] && + (ijk[2] & int32_t(~Node1T::MASK)) == mKeys[0][2]; + } +- __hostdev__ bool isCached2(const CoordType& ijk) const ++ __hostdev__ bool isCached2(__global__ const CoordType& ijk) const __global__ + { + return (ijk[0] & int32_t(~Node2T::MASK)) == mKeys[1][0] && + (ijk[1] & int32_t(~Node2T::MASK)) == mKeys[1][1] && +@@ -4999,12 +5968,12 @@ + } + #endif + +- __hostdev__ ValueType getValue(const CoordType& ijk) const ++ __hostdev__ ValueType getValue(__global__ const CoordType& ijk) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached1(dirty)) { + return mNode1->getValueAndCache(ijk, *this); +@@ -5013,21 +5982,37 @@ + } + return mRoot->getValueAndCache(ijk, *this); + } +- __hostdev__ ValueType operator()(const CoordType& ijk) const ++#if defined(__KERNEL_METAL__) ++ __hostdev__ ValueType getValue(__local__ const CoordType& ijk) const __global__ + { ++#ifdef USE_SINGLE_ACCESSOR_KEY ++ const CoordValueType dirty = this->computeDirty(ijk); ++#else ++ __global__ auto&& dirty = ijk; ++#endif ++ if (this->isCached1(dirty)) { ++ return mNode1->getValueAndCache(ijk, *this); ++ } else if (this->isCached2(dirty)) { ++ return mNode2->getValueAndCache(ijk, *this); ++ } ++ return mRoot->getValueAndCache(ijk, *this); ++ } ++#endif ++ __hostdev__ ValueType operator()(__global__ const CoordType& ijk) const __global__ ++ { + return this->getValue(ijk); + } +- __hostdev__ ValueType operator()(int i, int j, int k) const ++ __hostdev__ ValueType operator()(int i, int j, int k) const __global__ + { + return this->getValue(CoordType(i,j,k)); + } + +- __hostdev__ NodeInfo getNodeInfo(const CoordType& ijk) const ++ __hostdev__ NodeInfo getNodeInfo(__global__ const CoordType& ijk) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached1(dirty)) { + return mNode1->getNodeInfoAndCache(ijk, *this); +@@ -5037,12 +6022,12 @@ + return mRoot->getNodeInfoAndCache(ijk, *this); + } + +- __hostdev__ bool isActive(const CoordType& ijk) const ++ __hostdev__ bool isActive(__global__ const CoordType& ijk) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached1(dirty)) { + return mNode1->isActiveAndCache(ijk, *this); +@@ -5052,12 +6037,12 @@ + return mRoot->isActiveAndCache(ijk, *this); + } + +- __hostdev__ bool probeValue(const CoordType& ijk, ValueType& v) const ++ __hostdev__ bool probeValue(__global__ const CoordType& ijk, __global__ ValueType& v) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached1(dirty)) { + return mNode1->probeValueAndCache(ijk, v, *this); +@@ -5067,12 +6052,12 @@ + return mRoot->probeValueAndCache(ijk, v, *this); + } + +- __hostdev__ const LeafT* probeLeaf(const CoordType& ijk) const ++ __hostdev__ __global__ const LeafT* probeLeaf(__global__ const CoordType& ijk) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached1(dirty)) { + return mNode1->probeLeafAndCache(ijk, *this); +@@ -5083,12 +6068,12 @@ + } + + template +- __hostdev__ uint32_t getDim(const CoordType& ijk, const RayT& ray) const ++ __hostdev__ uint32_t getDim(__global__ const CoordType& ijk, __global__ const RayT& ray) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached1(dirty)) { + return mNode1->getDimAndCache(ijk, ray, *this); +@@ -5108,7 +6093,7 @@ + friend class LeafNode; + + /// @brief Inserts a leaf node and key pair into this ReadAccessor +- __hostdev__ void insert(const CoordType& ijk, const Node1T* node) const ++ __hostdev__ void insert(__global__ const CoordType& ijk, __global__ const Node1T* node) const __local__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + mKey = ijk; +@@ -5117,7 +6102,7 @@ + #endif + mNode1 = node; + } +- __hostdev__ void insert(const CoordType& ijk, const Node2T* node) const ++ __hostdev__ void insert(__local__ const CoordType& ijk, __global__ const Node2T* node) const __local__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + mKey = ijk; +@@ -5127,7 +6112,11 @@ + mNode2 = node; + } + template +- __hostdev__ void insert(const CoordType&, const OtherNodeT*) const {} ++ __hostdev__ void insert(__global__ const CoordType&, __global__ const OtherNodeT*) const __local__ {} ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ void insert(__local__ const CoordType&, __global__ const OtherNodeT*) const __local__ {} ++#endif + }; // ReadAccessor + + +@@ -5145,7 +6134,7 @@ + using ValueT = typename RootT::ValueType; + + using FloatType = typename RootT::FloatType; +- using CoordValueType = typename RootT::CoordT::ValueType; ++ using CoordValueType = typename RootT::CoordType::ValueType; + + // All member data are mutable to allow for access methods to be const + #ifdef USE_SINGLE_ACCESSOR_KEY // 44 bytes total +@@ -5153,19 +6142,19 @@ + #else // 68 bytes total + mutable CoordT mKeys[3]; // 3*3*4 = 36 bytes + #endif +- mutable const RootT* mRoot; +- mutable const void* mNode[3]; // 4*8 = 32 bytes ++ mutable __global__ const RootT* mRoot; ++ mutable __global__ const void* mNode[3]; // 4*8 = 32 bytes + + public: + using ValueType = ValueT; + using CoordType = CoordT; + +- static const int CacheLevels = 3; ++ static __constant__ const int CacheLevels = 3; + + using NodeInfo = typename ReadAccessor::NodeInfo; + + /// @brief Constructor from a root node +- __hostdev__ ReadAccessor(const RootT& root) ++ __hostdev__ ReadAccessor(__global__ const RootT& root) __local__ + #ifdef USE_SINGLE_ACCESSOR_KEY + : mKey(CoordType::max()) + #else +@@ -5177,35 +6166,38 @@ + } + + /// @brief Constructor from a grid +- __hostdev__ ReadAccessor(const GridT& grid) : ReadAccessor(grid.tree().root()) {} ++ __hostdev__ ReadAccessor(__global__ const GridT& grid) __local__ : ReadAccessor(grid.tree().root()) {} + + /// @brief Constructor from a tree +- __hostdev__ ReadAccessor(const TreeT& tree) : ReadAccessor(tree.root()) {} ++ __hostdev__ ReadAccessor(__global__ const TreeT& tree) __local__ : ReadAccessor(tree.root()) {} + +- __hostdev__ const RootT& root() const { return *mRoot; } ++ __hostdev__ __global__ const RootT& root() const __global__ { return *mRoot; } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ __global__ const RootT& root() const __local__ { return *mRoot; } ++#endif + + /// @brief Defaults constructors +- ReadAccessor(const ReadAccessor&) = default; +- ~ReadAccessor() = default; +- ReadAccessor& operator=(const ReadAccessor&) = default; ++ ReadAccessor(__local__ const ReadAccessor&) __local__ = default; ++ ~ReadAccessor() __global__ = default; ++ __global__ ReadAccessor& operator=(__global__ const ReadAccessor&) __global__ = default; + + /// @brief Return a const point to the cached node of the specified type + /// + /// @warning The return value could be NULL. + template +- __hostdev__ const NodeT* getNode() const ++ __hostdev__ __global__ const NodeT* getNode() const __global__ + { + using T = typename NodeTrait::type; + static_assert(is_same::value, "ReadAccessor::getNode: Invalid node type"); +- return reinterpret_cast(mNode[NodeT::LEVEL]); ++ return reinterpret_cast<__global__ const T*>(mNode[NodeT::LEVEL]); + } + + template +- __hostdev__ const typename NodeTrait::type* getNode() const ++ __hostdev__ __global__ const typename NodeTrait::type* getNode() const + { + using T = typename NodeTrait::type; + static_assert(LEVEL>=0 && LEVEL<=2, "ReadAccessor::getNode: Invalid node type"); +- return reinterpret_cast(mNode[LEVEL]); ++ return reinterpret_cast<__global__ const T*>(mNode[LEVEL]); + } + + +@@ -5222,7 +6214,7 @@ + + #ifdef USE_SINGLE_ACCESSOR_KEY + template +- __hostdev__ bool isCached(CoordValueType dirty) const ++ __hostdev__ bool isCached(CoordValueType dirty) const __global__ + { + if (!mNode[NodeT::LEVEL]) + return false; +@@ -5233,128 +6225,229 @@ + return true; + } + +- __hostdev__ CoordValueType computeDirty(const CoordType& ijk) const ++ __hostdev__ CoordValueType computeDirty(const CoordType& ijk) const __global__ + { + return (ijk[0] ^ mKey[0]) | (ijk[1] ^ mKey[1]) | (ijk[2] ^ mKey[2]); + } + #else + template +- __hostdev__ bool isCached(const CoordType& ijk) const ++ __hostdev__ bool isCached(__global__ const CoordType& ijk) const __global__ + { + return (ijk[0] & int32_t(~NodeT::MASK)) == mKeys[NodeT::LEVEL][0] && (ijk[1] & int32_t(~NodeT::MASK)) == mKeys[NodeT::LEVEL][1] && (ijk[2] & int32_t(~NodeT::MASK)) == mKeys[NodeT::LEVEL][2]; + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ bool isCached(__local__ const CoordType& ijk) const __global__ ++ { ++ return (ijk[0] & int32_t(~NodeT::MASK)) == mKeys[NodeT::LEVEL][0] && (ijk[1] & int32_t(~NodeT::MASK)) == mKeys[NodeT::LEVEL][1] && (ijk[2] & int32_t(~NodeT::MASK)) == mKeys[NodeT::LEVEL][2]; ++ } ++ template ++ __hostdev__ bool isCached(__local__ const CoordType& ijk) const __local__ ++ { ++ return (ijk[0] & int32_t(~NodeT::MASK)) == mKeys[NodeT::LEVEL][0] && (ijk[1] & int32_t(~NodeT::MASK)) == mKeys[NodeT::LEVEL][1] && (ijk[2] & int32_t(~NodeT::MASK)) == mKeys[NodeT::LEVEL][2]; ++ } ++#endif // __KERNEL_METAL__ + #endif + +- __hostdev__ ValueType getValue(const CoordType& ijk) const ++ __hostdev__ ValueType getValue(__global__ const CoordType& ijk) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached(dirty)) { +- return ((LeafT*)mNode[0])->getValue(ijk); ++ return ((__global__ LeafT*)mNode[0])->getValue(ijk); + } else if (this->isCached(dirty)) { +- return ((NodeT1*)mNode[1])->getValueAndCache(ijk, *this); ++ return ((__global__ NodeT1*)mNode[1])->getValueAndCache(ijk, *this); + } else if (this->isCached(dirty)) { +- return ((NodeT2*)mNode[2])->getValueAndCache(ijk, *this); ++ return ((__global__ NodeT2*)mNode[2])->getValueAndCache(ijk, *this); + } + return mRoot->getValueAndCache(ijk, *this); + } +- __hostdev__ ValueType operator()(const CoordType& ijk) const ++#if defined(__KERNEL_METAL__) ++ __hostdev__ ValueType getValue(__local__ const CoordType& ijk) const __global__ + { ++#ifdef USE_SINGLE_ACCESSOR_KEY ++ const CoordValueType dirty = this->computeDirty(ijk); ++#else ++ __local__ auto&& dirty = ijk; ++#endif ++ if (this->isCached(dirty)) { ++ return ((__global__ LeafT*)mNode[0])->getValue(ijk); ++ } else if (this->isCached(dirty)) { ++ return ((__global__ NodeT1*)mNode[1])->getValueAndCache(ijk, *this); ++ } else if (this->isCached(dirty)) { ++ return ((__global__ NodeT2*)mNode[2])->getValueAndCache(ijk, *this); ++ } ++ return mRoot->getValueAndCache(ijk, *this); ++ } ++ __hostdev__ ValueType getValue(__local__ const CoordType& ijk) const __local__ ++ { ++#ifdef USE_SINGLE_ACCESSOR_KEY ++ const CoordValueType dirty = this->computeDirty(ijk); ++#else ++ __local__ auto&& dirty = ijk; ++#endif ++ if (this->isCached(dirty)) { ++ return ((__global__ LeafT*)mNode[0])->getValue(ijk); ++ } else if (this->isCached(dirty)) { ++ return ((__global__ NodeT1*)mNode[1])->getValueAndCache(ijk, *this); ++ } else if (this->isCached(dirty)) { ++ return ((__global__ NodeT2*)mNode[2])->getValueAndCache(ijk, *this); ++ } ++ return mRoot->getValueAndCache(ijk, *this); ++ } ++#endif // __KERNEL_METAL__ ++ ++ __hostdev__ ValueType operator()(__global__ const CoordType& ijk) const __global__ ++ { + return this->getValue(ijk); + } +- __hostdev__ ValueType operator()(int i, int j, int k) const ++ __hostdev__ ValueType operator()(int i, int j, int k) const __global__ + { + return this->getValue(CoordType(i,j,k)); + } + +- __hostdev__ NodeInfo getNodeInfo(const CoordType& ijk) const ++ __hostdev__ NodeInfo getNodeInfo(__global__ const CoordType& ijk) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached(dirty)) { +- return ((LeafT*)mNode[0])->getNodeInfoAndCache(ijk, *this); ++ return ((__global__ LeafT*)mNode[0])->getNodeInfoAndCache(ijk, *this); + } else if (this->isCached(dirty)) { +- return ((NodeT1*)mNode[1])->getNodeInfoAndCache(ijk, *this); ++ return ((__global__ NodeT1*)mNode[1])->getNodeInfoAndCache(ijk, *this); + } else if (this->isCached(dirty)) { +- return ((NodeT2*)mNode[2])->getNodeInfoAndCache(ijk, *this); ++ return ((__global__ NodeT2*)mNode[2])->getNodeInfoAndCache(ijk, *this); + } + return mRoot->getNodeInfoAndCache(ijk, *this); + } + +- __hostdev__ bool isActive(const CoordType& ijk) const ++ __hostdev__ bool isActive(__global__ const CoordType& ijk) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached(dirty)) { +- return ((LeafT*)mNode[0])->isActive(ijk); ++ return ((__global__ LeafT*)mNode[0])->isActive(ijk); + } else if (this->isCached(dirty)) { +- return ((NodeT1*)mNode[1])->isActiveAndCache(ijk, *this); ++ return ((__global__ NodeT1*)mNode[1])->isActiveAndCache(ijk, *this); + } else if (this->isCached(dirty)) { +- return ((NodeT2*)mNode[2])->isActiveAndCache(ijk, *this); ++ return ((__global__ NodeT2*)mNode[2])->isActiveAndCache(ijk, *this); + } + return mRoot->isActiveAndCache(ijk, *this); + } ++#if defined(__KERNEL_METAL__) ++ __hostdev__ bool isActive(__local__ const CoordType& ijk) const __local__ ++ { ++#ifdef USE_SINGLE_ACCESSOR_KEY ++ const CoordValueType dirty = this->computeDirty(ijk); ++#else ++ __local__ auto&& dirty = ijk; ++#endif ++ if (this->isCached(dirty)) { ++ return ((__global__ LeafT*)mNode[0])->isActive(ijk); ++ } else if (this->isCached(dirty)) { ++ return ((__global__ NodeT1*)mNode[1])->isActiveAndCache(ijk, *this); ++ } else if (this->isCached(dirty)) { ++ return ((__global__ NodeT2*)mNode[2])->isActiveAndCache(ijk, *this); ++ } ++ return mRoot->isActiveAndCache(ijk, *this); ++ } ++#endif + +- __hostdev__ bool probeValue(const CoordType& ijk, ValueType& v) const ++ __hostdev__ bool probeValue(__global__ const CoordType& ijk, __global__ ValueType& v) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached(dirty)) { +- return ((LeafT*)mNode[0])->probeValue(ijk, v); ++ return ((__global__ LeafT*)mNode[0])->probeValue(ijk, v); + } else if (this->isCached(dirty)) { +- return ((NodeT1*)mNode[1])->probeValueAndCache(ijk, v, *this); ++ return ((__global__ NodeT1*)mNode[1])->probeValueAndCache(ijk, v, *this); + } else if (this->isCached(dirty)) { +- return ((NodeT2*)mNode[2])->probeValueAndCache(ijk, v, *this); ++ return ((__global__ NodeT2*)mNode[2])->probeValueAndCache(ijk, v, *this); + } + return mRoot->probeValueAndCache(ijk, v, *this); + } + +- __hostdev__ const LeafT* probeLeaf(const CoordType& ijk) const ++ __hostdev__ __global__ const LeafT* probeLeaf(__global__ const CoordType& ijk) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached(dirty)) { +- return ((LeafT*)mNode[0]); ++ return ((__global__ LeafT*)mNode[0]); + } else if (this->isCached(dirty)) { +- return ((NodeT1*)mNode[1])->probeLeafAndCache(ijk, *this); ++ return ((__global__ NodeT1*)mNode[1])->probeLeafAndCache(ijk, *this); + } else if (this->isCached(dirty)) { +- return ((NodeT2*)mNode[2])->probeLeafAndCache(ijk, *this); ++ return ((__global__ NodeT2*)mNode[2])->probeLeafAndCache(ijk, *this); + } + return mRoot->probeLeafAndCache(ijk, *this); + } + + template +- __hostdev__ uint32_t getDim(const CoordType& ijk, const RayT& ray) const ++ __hostdev__ uint32_t getDim(__global__ const CoordType& ijk, __global__ const RayT& ray) const __global__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + const CoordValueType dirty = this->computeDirty(ijk); + #else +- auto&& dirty = ijk; ++ __global__ auto&& dirty = ijk; + #endif + if (this->isCached(dirty)) { +- return ((LeafT*)mNode[0])->getDimAndCache(ijk, ray, *this); ++ return ((__global__ LeafT*)mNode[0])->getDimAndCache(ijk, ray, *this); + } else if (this->isCached(dirty)) { +- return ((NodeT1*)mNode[1])->getDimAndCache(ijk, ray, *this); ++ return ((__global__ NodeT1*)mNode[1])->getDimAndCache(ijk, ray, *this); + } else if (this->isCached(dirty)) { +- return ((NodeT2*)mNode[2])->getDimAndCache(ijk, ray, *this); ++ return ((__global__ NodeT2*)mNode[2])->getDimAndCache(ijk, ray, *this); + } + return mRoot->getDimAndCache(ijk, ray, *this); + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ uint32_t getDim(__global__ const CoordType& ijk, __local__ const RayT& ray) const __global__ ++ { ++#ifdef USE_SINGLE_ACCESSOR_KEY ++ const CoordValueType dirty = this->computeDirty(ijk); ++#else ++ __global__ auto&& dirty = ijk; ++#endif ++ if (this->isCached(dirty)) { ++ return ((__global__ LeafT*)mNode[0])->getDimAndCache(ijk, ray, *this); ++ } else if (this->isCached(dirty)) { ++ return ((__global__ NodeT1*)mNode[1])->getDimAndCache(ijk, ray, *this); ++ } else if (this->isCached(dirty)) { ++ return ((__global__ NodeT2*)mNode[2])->getDimAndCache(ijk, ray, *this); ++ } ++ return mRoot->getDimAndCache(ijk, ray, *this); ++ } ++ template ++ __hostdev__ uint32_t getDim(__local__ const CoordType& ijk, __local__ const RayT& ray) const __local__ ++ { ++#ifdef USE_SINGLE_ACCESSOR_KEY ++ const CoordValueType dirty = this->computeDirty(ijk); ++#else ++ __local__ auto&& dirty = ijk; ++#endif ++ if (this->isCached(dirty)) { ++ return ((__global__ LeafT*)mNode[0])->getDimAndCache(ijk, ray, *this); ++ } else if (this->isCached(dirty)) { ++ return ((__global__ NodeT1*)mNode[1])->getDimAndCache(ijk, ray, *this); ++ } else if (this->isCached(dirty)) { ++ return ((__global__ NodeT2*)mNode[2])->getDimAndCache(ijk, ray, *this); ++ } ++ return mRoot->getDimAndCache(ijk, ray, *this); ++ } ++#endif // __KERNEL_METAL__ + + private: + /// @brief Allow nodes to insert themselves into the cache. +@@ -5367,7 +6460,7 @@ + + /// @brief Inserts a leaf node and key pair into this ReadAccessor + template +- __hostdev__ void insert(const CoordType& ijk, const NodeT* node) const ++ __hostdev__ void insert(__global__ const CoordType& ijk, __global__ const NodeT* node) const __local__ + { + #ifdef USE_SINGLE_ACCESSOR_KEY + mKey = ijk; +@@ -5376,6 +6469,28 @@ + #endif + mNode[NodeT::LEVEL] = node; + } ++#if defined(__KERNEL_METAL__) ++ template ++ __hostdev__ void insert(__local__ const CoordType& ijk, __global__ const NodeT* node) const __local__ ++ { ++#ifdef USE_SINGLE_ACCESSOR_KEY ++ mKey = ijk; ++#else ++ mKeys[NodeT::LEVEL] = ijk & ~NodeT::MASK; ++#endif ++ mNode[NodeT::LEVEL] = node; ++ } ++ template ++ __hostdev__ void insert(__local__ const CoordType& ijk, __global__ const NodeT* node) const __global__ ++ { ++#ifdef USE_SINGLE_ACCESSOR_KEY ++ mKey = ijk; ++#else ++ mKeys[NodeT::LEVEL] = ijk & ~NodeT::MASK; ++#endif ++ mNode[NodeT::LEVEL] = node; ++ } ++#endif // __KERNEL_METAL__ + }; // ReadAccessor + + ////////////////////////////////////////////////// +@@ -5393,19 +6508,19 @@ + /// createAccessor<0,1,2>(grid): Caching of all nodes at all tree levels + + template +-ReadAccessor createAccessor(const NanoGrid &grid) ++ReadAccessor createAccessor(__global__ const NanoGrid &grid) + { + return ReadAccessor(grid); + } + + template +-ReadAccessor createAccessor(const NanoTree &tree) ++ReadAccessor createAccessor(__global__ const NanoTree &tree) + { + return ReadAccessor(tree); + } + + template +-ReadAccessor createAccessor(const NanoRoot &root) ++ReadAccessor createAccessor(__global__ const NanoRoot &root) + { + return ReadAccessor(root); + } +@@ -5424,52 +6539,59 @@ + // memory-layout of the data structure and the reasons why certain methods are safe + // to call and others are not! + using GridT = NanoGrid; +- __hostdev__ const GridT& grid() const { return *reinterpret_cast(this); } ++ __hostdev__ __global__ const GridT& grid() const __global__ { return *reinterpret_cast<__global__ const GridT*>(this); } + + public: +- __hostdev__ bool isValid() const { return this->grid().isValid(); } +- __hostdev__ uint64_t gridSize() const { return this->grid().gridSize(); } +- __hostdev__ uint32_t gridIndex() const { return this->grid().gridIndex(); } +- __hostdev__ uint32_t gridCount() const { return this->grid().gridCount(); } +- __hostdev__ const char* shortGridName() const { return this->grid().shortGridName(); } +- __hostdev__ GridType gridType() const { return this->grid().gridType(); } +- __hostdev__ GridClass gridClass() const { return this->grid().gridClass(); } +- __hostdev__ bool isLevelSet() const { return this->grid().isLevelSet(); } +- __hostdev__ bool isFogVolume() const { return this->grid().isFogVolume(); } +- __hostdev__ bool isPointIndex() const { return this->grid().isPointIndex(); } +- __hostdev__ bool isPointData() const { return this->grid().isPointData(); } +- __hostdev__ bool isMask() const { return this->grid().isMask(); } +- __hostdev__ bool isStaggered() const { return this->grid().isStaggered(); } +- __hostdev__ bool isUnknown() const { return this->grid().isUnknown(); } +- __hostdev__ const Map& map() const { return this->grid().map(); } +- __hostdev__ const BBox& worldBBox() const { return this->grid().worldBBox(); } +- __hostdev__ const BBox& indexBBox() const { return this->grid().indexBBox(); } +- __hostdev__ Vec3R voxelSize() const { return this->grid().voxelSize(); } +- __hostdev__ int blindDataCount() const { return this->grid().blindDataCount(); } +- __hostdev__ const GridBlindMetaData& blindMetaData(uint32_t n) const { return this->grid().blindMetaData(n); } +- __hostdev__ uint64_t activeVoxelCount() const { return this->grid().activeVoxelCount(); } +- __hostdev__ const uint32_t& activeTileCount(uint32_t level) const { return this->grid().tree().activeTileCount(level); } +- __hostdev__ uint32_t nodeCount(uint32_t level) const { return this->grid().tree().nodeCount(level); } +- __hostdev__ uint64_t checksum() const { return this->grid().checksum(); } +- __hostdev__ bool isEmpty() const { return this->grid().isEmpty(); } +- __hostdev__ Version version() const { return this->grid().version(); } ++ __hostdev__ bool isValid() const __global__ { return this->grid().isValid(); } ++ __hostdev__ uint64_t gridSize() const __global__ { return this->grid().gridSize(); } ++ __hostdev__ uint32_t gridIndex() const __global__ { return this->grid().gridIndex(); } ++ __hostdev__ uint32_t gridCount() const __global__ { return this->grid().gridCount(); } ++ __hostdev__ __global__ const char* shortGridName() const __global__ { return this->grid().shortGridName(); } ++ __hostdev__ GridType gridType() const __global__ { return this->grid().gridType(); } ++ __hostdev__ GridClass gridClass() const __global__ { return this->grid().gridClass(); } ++ __hostdev__ bool isLevelSet() const __global__ { return this->grid().isLevelSet(); } ++ __hostdev__ bool isFogVolume() const __global__ { return this->grid().isFogVolume(); } ++ __hostdev__ bool isPointIndex() const __global__ { return this->grid().isPointIndex(); } ++ __hostdev__ bool isPointData() const __global__ { return this->grid().isPointData(); } ++ __hostdev__ bool isMask() const __global__ { return this->grid().isMask(); } ++ __hostdev__ bool isStaggered() const __global__ { return this->grid().isStaggered(); } ++ __hostdev__ bool isUnknown() const __global__ { return this->grid().isUnknown(); } ++ __hostdev__ __global__ const Map& map() const __global__ { return this->grid().map(); } ++ __hostdev__ __global__ const BBox& worldBBox() const __global__ { return this->grid().worldBBox(); } ++ __hostdev__ __global__ const BBox& indexBBox() const __global__ { return this->grid().indexBBox(); } ++ __hostdev__ Vec3R voxelSize() const __global__ { return this->grid().voxelSize(); } ++ __hostdev__ int blindDataCount() const __global__ { return this->grid().blindDataCount(); } ++ __hostdev__ __global__ const GridBlindMetaData& blindMetaData(uint32_t n) const __global__ { return this->grid().blindMetaData(n); } ++ __hostdev__ uint64_t activeVoxelCount() const __global__ { return this->grid().activeVoxelCount(); } ++ __hostdev__ __global__ const uint32_t& activeTileCount(uint32_t level) const __global__ { return this->grid().tree().activeTileCount(level); } ++ __hostdev__ uint32_t nodeCount(uint32_t level) const __global__ { return this->grid().tree().nodeCount(level); } ++ __hostdev__ uint64_t checksum() const __global__ { return this->grid().checksum(); } ++ __hostdev__ bool isEmpty() const __global__ { return this->grid().isEmpty(); } ++ __hostdev__ Version version() const __global__ { return this->grid().version(); } + }; // GridMetaData + + /// @brief Class to access points at a specific voxel location + template +-class PointAccessor : public DefaultReadAccessor ++class PointAccessor ++#if !defined(__KERNEL_METAL__) ++ : public DefaultReadAccessor ++#endif + { ++#if defined(__KERNEL_METAL__) ++ DefaultReadAccessor AccT; ++#else + using AccT = DefaultReadAccessor; +- const UInt32Grid* mGrid; +- const AttT* mData; ++#endif ++ const __global__ UInt32Grid* mGrid; ++ const __global__ AttT* mData; + + public: + using LeafNodeType = typename NanoRoot::LeafNodeType; + +- PointAccessor(const UInt32Grid& grid) ++ PointAccessor(__global__ const UInt32Grid& grid) __local__ + : AccT(grid.tree().root()) + , mGrid(&grid) +- , mData(reinterpret_cast(grid.blindData(0))) ++ , mData(reinterpret_cast<__global__ const AttT*>(grid.blindData(0))) + { + NANOVDB_ASSERT(grid.gridType() == GridType::UInt32); + NANOVDB_ASSERT((grid.gridClass() == GridClass::PointIndex && is_same::value) || +@@ -5478,7 +6600,7 @@ + } + /// @brief Return the total number of point in the grid and set the + /// iterators to the complete range of points. +- __hostdev__ uint64_t gridPoints(const AttT*& begin, const AttT*& end) const ++ __hostdev__ uint64_t gridPoints(__global__ const AttT*& begin, __global__ const AttT*& end) const __global__ + { + const uint64_t count = mGrid->blindMetaData(0u).mElementCount; + begin = mData; +@@ -5488,9 +6610,9 @@ + /// @brief Return the number of points in the leaf node containing the coordinate @a ijk. + /// If this return value is larger than zero then the iterators @a begin and @a end + /// will point to all the attributes contained within that leaf node. +- __hostdev__ uint64_t leafPoints(const Coord& ijk, const AttT*& begin, const AttT*& end) const ++ __hostdev__ uint64_t leafPoints(__global__ const Coord& ijk, __global__ const AttT*& begin, __global__ const AttT*& end) const __global__ + { +- auto* leaf = this->probeLeaf(ijk); ++ __global__ auto* leaf = this->probeLeaf(ijk); + if (leaf == nullptr) { + return 0; + } +@@ -5500,14 +6622,14 @@ + } + + /// @brief get iterators over offsets to points at a specific voxel location +- __hostdev__ uint64_t voxelPoints(const Coord& ijk, const AttT*& begin, const AttT*& end) const ++ __hostdev__ uint64_t voxelPoints(__global__ const Coord& ijk, __global__ const AttT*& begin, __global__ const AttT*& end) const __global__ + { +- auto* leaf = this->probeLeaf(ijk); ++ __global__ auto* leaf = this->probeLeaf(ijk); + if (leaf == nullptr) + return 0; + const uint32_t offset = LeafNodeType::CoordToOffset(ijk); + if (leaf->isActive(offset)) { +- auto* p = mData + leaf->minimum(); ++ __global__ auto* p = mData + leaf->minimum(); + begin = p + (offset == 0 ? 0 : leaf->getValue(offset - 1)); + end = p + leaf->getValue(offset); + return end - begin; +@@ -5520,11 +6642,20 @@ + /// + /// @note The ChannelT template parameter can be either const and non-const. + template +-class ChannelAccessor : public DefaultReadAccessor ++class ChannelAccessor ++#if !defined (__KERNEL_METAL__) ++ : public DefaultReadAccessor ++#endif + { ++#if defined (__KERNEL_METAL__) ++ DefaultReadAccessor BaseT; ++#define BASE(v) BaseT.v ++#else + using BaseT = DefaultReadAccessor; +- const IndexGrid &mGrid; +- ChannelT *mChannel; ++#define BASE(v) BaseT::v ++#endif ++ __global__ const IndexGrid &mGrid; ++ __global__ ChannelT *mChannel; + + public: + using ValueType = ChannelT; +@@ -5533,7 +6664,7 @@ + + /// @brief Ctor from an IndexGrid and an integer ID of an internal channel + /// that is assumed to exist as blind data in the IndexGrid. +- __hostdev__ ChannelAccessor(const IndexGrid& grid, uint32_t channelID = 0u) ++ __hostdev__ ChannelAccessor(__global__ const IndexGrid& grid, uint32_t channelID = 0u) + : BaseT(grid.tree().root()) + , mGrid(grid) + , mChannel(nullptr) +@@ -5544,7 +6675,7 @@ + } + + /// @brief Ctor from an IndexGrid and an external channel +- __hostdev__ ChannelAccessor(const IndexGrid& grid, ChannelT *channelPtr) ++ __hostdev__ ChannelAccessor(__global__ const IndexGrid& grid, __global__ ChannelT *channelPtr) + : BaseT(grid.tree().root()) + , mGrid(grid) + , mChannel(channelPtr) +@@ -5555,19 +6686,19 @@ + } + + /// @brief Return a const reference to the IndexGrid +- __hostdev__ const IndexGrid &grid() const {return mGrid;} ++ __hostdev__ __global__ const IndexGrid &grid() const {return mGrid;} + + /// @brief Return a const reference to the tree of the IndexGrid +- __hostdev__ const IndexTree &tree() const {return mGrid.tree();} ++ __hostdev__ __global__ const IndexTree &tree() const {return mGrid.tree();} + + /// @brief Return a vector of the axial voxel sizes +- __hostdev__ const Vec3R& voxelSize() const { return mGrid.voxelSize(); } ++ __hostdev__ __global__ const Vec3R& voxelSize() const { return mGrid.voxelSize(); } + + /// @brief Return total number of values indexed by the IndexGrid +- __hostdev__ const uint64_t& valueCount() const { return mGrid.valueCount(); } ++ __hostdev__ uint64_t valueCount() const { return mGrid.valueCount(); } + + /// @brief Change to an external channel +- __hostdev__ void setChannel(ChannelT *channelPtr) ++ __hostdev__ void setChannel(__global__ ChannelT *channelPtr) + { + mChannel = channelPtr; + NANOVDB_ASSERT(mChannel); +@@ -5577,23 +6708,24 @@ + /// in the IndexGrid. + __hostdev__ void setChannel(uint32_t channelID) + { +- this->setChannel(reinterpret_cast(const_cast(mGrid.blindData(channelID)))); ++ this->setChannel(reinterpret_cast<__global__ ChannelT*>(const_cast<__global__ void*>(mGrid.blindData(channelID)))); + } + + /// @brief Return the linear offset into a channel that maps to the specified coordinate +- __hostdev__ uint64_t getIndex(const Coord& ijk) const {return BaseT::getValue(ijk);} +- __hostdev__ uint64_t idx(int i, int j, int k) const {return BaseT::getValue(Coord(i,j,k));} ++ __hostdev__ uint64_t getIndex(__global__ const Coord& ijk) const {return BASE(getValue)(ijk);} ++ __hostdev__ uint64_t idx(int i, int j, int k) const {return BASE(getValue)(Coord(i,j,k));} + + /// @brief Return the value from a cached channel that maps to the specified coordinate +- __hostdev__ ChannelT& getValue(const Coord& ijk) const {return mChannel[BaseT::getValue(ijk)];} +- __hostdev__ ChannelT& operator()(const Coord& ijk) const {return this->getValue(ijk);} +- __hostdev__ ChannelT& operator()(int i, int j, int k) const {return this->getValue(Coord(i,j,k));} ++ __hostdev__ __global__ ChannelT& getValue(__global__ const Coord& ijk) const {return mChannel[BASE(getValue)(ijk)];} ++ __hostdev__ __global__ ChannelT& operator()(__global__ const Coord& ijk) const {return this->getValue(ijk);} ++ __hostdev__ __global__ ChannelT& operator()(int i, int j, int k) const {return this->getValue(Coord(i,j,k));} + + /// @brief return the state and updates the value of the specified voxel +- __hostdev__ bool probeValue(const CoordType& ijk, typename remove_const::type &v) const ++ using CoordType = DefaultReadAccessor::CoordType; ++ __hostdev__ bool probeValue(__global__ const CoordType& ijk, __global__ typename remove_const::type &v) const + { + uint64_t idx; +- const bool isActive = BaseT::probeValue(ijk, idx); ++ const bool isActive = BASE(probeValue)(ijk, idx); + v = mChannel[idx]; + return isActive; + } +@@ -5601,7 +6733,7 @@ + /// + /// @note The template parameter can be either const or non-const + template +- __hostdev__ T& getValue(const Coord& ijk, T* channelPtr) const {return channelPtr[BaseT::getValue(ijk)];} ++ __hostdev__ __global__ T& getValue(__global__ const Coord& ijk, __global__ T* channelPtr) const {return channelPtr[BASE(getValue)(ijk)];} + + }; // ChannelAccessor + +@@ -5643,6 +6775,7 @@ + /// @throw std::invalid_argument if buffer does not point to a valid NanoVDB grid. + /// + /// @warning This is pretty ugly code that involves lots of pointer and bit manipulations - not for the faint of heart :) ++#if !defined(__KERNEL_METAL__) + template // StreamT class must support: "void write(char*, size_t)" + void writeUncompressedGrid(StreamT &os, const void *buffer) + { +@@ -5768,7 +6901,7 @@ + } + return readUncompressedGrids(is, buffer); + }// readUncompressedGrids +- ++#endif // #if !defined(__KERNEL_METAL__) + } // namespace io + + #endif// if !defined(__CUDA_ARCH__) && !defined(__HIP__) +Index: nanovdb/nanovdb/util/SampleFromVoxels.h +=================================================================== +--- nanovdb/nanovdb/util/SampleFromVoxels.h (revision 63221) ++++ nanovdb/nanovdb/util/SampleFromVoxels.h (working copy) +@@ -1,983 +1,1120 @@ +-// Copyright Contributors to the OpenVDB Project +-// SPDX-License-Identifier: MPL-2.0 +- +-////////////////////////////////////////////////////////////////////////// +-/// +-/// @file SampleFromVoxels.h +-/// +-/// @brief NearestNeighborSampler, TrilinearSampler, TriquadraticSampler and TricubicSampler +-/// +-/// @note These interpolators employ internal caching for better performance when used repeatedly +-/// in the same voxel location, so try to reuse an instance of these classes more than once. +-/// +-/// @warning While all the interpolators defined below work with both scalars and vectors +-/// values (e.g. float and Vec3) TrilinarSampler::zeroCrossing and +-/// Trilinear::gradient will only compile with floating point value types. +-/// +-/// @author Ken Museth +-/// +-/////////////////////////////////////////////////////////////////////////// +- +-#ifndef NANOVDB_SAMPLE_FROM_VOXELS_H_HAS_BEEN_INCLUDED +-#define NANOVDB_SAMPLE_FROM_VOXELS_H_HAS_BEEN_INCLUDED +- +-// Only define __hostdev__ when compiling as NVIDIA CUDA +-#if defined(__CUDACC__) || defined(__HIP__) +-#define __hostdev__ __host__ __device__ +-#else +-#include // for floor +-#define __hostdev__ +-#endif +- +-namespace nanovdb { +- +-// Forward declaration of sampler with specific polynomial orders +-template +-class SampleFromVoxels; +- +-/// @brief Factory free-function for a sampler of specific polynomial orders +-/// +-/// @details This allows for the compact syntax: +-/// @code +-/// auto acc = grid.getAccessor(); +-/// auto smp = nanovdb::createSampler<1>( acc ); +-/// @endcode +-template +-__hostdev__ SampleFromVoxels createSampler(const TreeOrAccT& acc) +-{ +- return SampleFromVoxels(acc); +-} +- +-/// @brief Utility function that returns the Coord of the round-down of @a xyz +-/// and redefined @xyz as the fractional part, ie xyz-in = return-value + xyz-out +-template class Vec3T> +-__hostdev__ inline CoordT Floor(Vec3T& xyz); +- +-/// @brief Template specialization of Floor for Vec3 +-template class Vec3T> +-__hostdev__ inline CoordT Floor(Vec3T& xyz) +-{ +- const float ijk[3] = {floorf(xyz[0]), floorf(xyz[1]), floorf(xyz[2])}; +- xyz[0] -= ijk[0]; +- xyz[1] -= ijk[1]; +- xyz[2] -= ijk[2]; +- return CoordT(int32_t(ijk[0]), int32_t(ijk[1]), int32_t(ijk[2])); +-} +- +-/// @brief Template specialization of Floor for Vec3 +-template class Vec3T> +-__hostdev__ inline CoordT Floor(Vec3T& xyz) +-{ +- const double ijk[3] = {floor(xyz[0]), floor(xyz[1]), floor(xyz[2])}; +- xyz[0] -= ijk[0]; +- xyz[1] -= ijk[1]; +- xyz[2] -= ijk[2]; +- return CoordT(int32_t(ijk[0]), int32_t(ijk[1]), int32_t(ijk[2])); +-} +- +-// ------------------------------> NearestNeighborSampler <-------------------------------------- +- +-/// @brief Nearest neighbor, i.e. zero order, interpolator with caching +-template +-class SampleFromVoxels +-{ +-public: +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +- +- static const int ORDER = 0; +- /// @brief Construction from a Tree or ReadAccessor +- __hostdev__ SampleFromVoxels(const TreeOrAccT& acc) +- : mAcc(acc) +- , mPos(CoordT::max()) +- { +- } +- +- __hostdev__ const TreeOrAccT& accessor() const { return mAcc; } +- +- /// @note xyz is in index space space +- template +- inline __hostdev__ ValueT operator()(const Vec3T& xyz) const; +- +- inline __hostdev__ ValueT operator()(const CoordT& ijk) const; +- +-private: +- const TreeOrAccT& mAcc; +- mutable CoordT mPos; +- mutable ValueT mVal; // private cache +-}; // SampleFromVoxels +- +-/// @brief Nearest neighbor, i.e. zero order, interpolator without caching +-template +-class SampleFromVoxels +-{ +-public: +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +- static const int ORDER = 0; +- +- /// @brief Construction from a Tree or ReadAccessor +- __hostdev__ SampleFromVoxels(const TreeOrAccT& acc) +- : mAcc(acc) +- { +- } +- +- __hostdev__ const TreeOrAccT& accessor() const { return mAcc; } +- +- /// @note xyz is in index space space +- template +- inline __hostdev__ ValueT operator()(const Vec3T& xyz) const; +- +- inline __hostdev__ ValueT operator()(const CoordT& ijk) const { return mAcc.getValue(ijk);} +- +-private: +- const TreeOrAccT& mAcc; +-}; // SampleFromVoxels +- +-template +-template +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(const Vec3T& xyz) const +-{ +- const CoordT ijk = Round(xyz); +- if (ijk != mPos) { +- mPos = ijk; +- mVal = mAcc.getValue(mPos); +- } +- return mVal; +-} +- +-template +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(const CoordT& ijk) const +-{ +- if (ijk != mPos) { +- mPos = ijk; +- mVal = mAcc.getValue(mPos); +- } +- return mVal; +-} +- +-template +-template +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(const Vec3T& xyz) const +-{ +- return mAcc.getValue(Round(xyz)); +-} +- +-// ------------------------------> TrilinearSampler <-------------------------------------- +- +-/// @brief Tri-linear sampler, i.e. first order, interpolator +-template +-class TrilinearSampler +-{ +-protected: +- const TreeOrAccT& mAcc; +- +-public: +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +- static const int ORDER = 1; +- +- /// @brief Protected constructor from a Tree or ReadAccessor +- __hostdev__ TrilinearSampler(const TreeOrAccT& acc) : mAcc(acc) {} +- +- __hostdev__ const TreeOrAccT& accessor() const { return mAcc; } +- +- /// @brief Extract the stencil of 8 values +- inline __hostdev__ void stencil(CoordT& ijk, ValueT (&v)[2][2][2]) const; +- +- template class Vec3T> +- static inline __hostdev__ ValueT sample(const Vec3T &uvw, const ValueT (&v)[2][2][2]); +- +- template class Vec3T> +- static inline __hostdev__ Vec3T gradient(const Vec3T &uvw, const ValueT (&v)[2][2][2]); +- +- static inline __hostdev__ bool zeroCrossing(const ValueT (&v)[2][2][2]); +-}; // TrilinearSamplerBase +- +-template +-__hostdev__ void TrilinearSampler::stencil(CoordT& ijk, ValueT (&v)[2][2][2]) const +-{ +- v[0][0][0] = mAcc.getValue(ijk); // i, j, k +- +- ijk[2] += 1; +- v[0][0][1] = mAcc.getValue(ijk); // i, j, k + 1 +- +- ijk[1] += 1; +- v[0][1][1] = mAcc.getValue(ijk); // i, j+1, k + 1 +- +- ijk[2] -= 1; +- v[0][1][0] = mAcc.getValue(ijk); // i, j+1, k +- +- ijk[0] += 1; +- ijk[1] -= 1; +- v[1][0][0] = mAcc.getValue(ijk); // i+1, j, k +- +- ijk[2] += 1; +- v[1][0][1] = mAcc.getValue(ijk); // i+1, j, k + 1 +- +- ijk[1] += 1; +- v[1][1][1] = mAcc.getValue(ijk); // i+1, j+1, k + 1 +- +- ijk[2] -= 1; +- v[1][1][0] = mAcc.getValue(ijk); // i+1, j+1, k +-} +- +-template +-template class Vec3T> +-__hostdev__ typename TreeOrAccT::ValueType TrilinearSampler::sample(const Vec3T &uvw, const ValueT (&v)[2][2][2]) +-{ +-#if 0 +- auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b-a, a); };// = w*(b-a) + a +- //auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b, fma(-w, a, a));};// = (1-w)*a + w*b +-#else +- auto lerp = [](ValueT a, ValueT b, RealT w) { return a + ValueT(w) * (b - a); }; +-#endif +- return lerp(lerp(lerp(v[0][0][0], v[0][0][1], uvw[2]), lerp(v[0][1][0], v[0][1][1], uvw[2]), uvw[1]), +- lerp(lerp(v[1][0][0], v[1][0][1], uvw[2]), lerp(v[1][1][0], v[1][1][1], uvw[2]), uvw[1]), +- uvw[0]); +-} +- +-template +-template class Vec3T> +-__hostdev__ Vec3T TrilinearSampler::gradient(const Vec3T &uvw, const ValueT (&v)[2][2][2]) +-{ +- static_assert(is_floating_point::value, "TrilinearSampler::gradient requires a floating-point type"); +-#if 0 +- auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b-a, a); };// = w*(b-a) + a +- //auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b, fma(-w, a, a));};// = (1-w)*a + w*b +-#else +- auto lerp = [](ValueT a, ValueT b, RealT w) { return a + ValueT(w) * (b - a); }; +-#endif +- +- ValueT D[4] = {v[0][0][1] - v[0][0][0], v[0][1][1] - v[0][1][0], v[1][0][1] - v[1][0][0], v[1][1][1] - v[1][1][0]}; +- +- // Z component +- Vec3T grad(0, 0, lerp(lerp(D[0], D[1], uvw[1]), lerp(D[2], D[3], uvw[1]), uvw[0])); +- +- const ValueT w = ValueT(uvw[2]); +- D[0] = v[0][0][0] + D[0] * w; +- D[1] = v[0][1][0] + D[1] * w; +- D[2] = v[1][0][0] + D[2] * w; +- D[3] = v[1][1][0] + D[3] * w; +- +- // X component +- grad[0] = lerp(D[2], D[3], uvw[1]) - lerp(D[0], D[1], uvw[1]); +- +- // Y component +- grad[1] = lerp(D[1] - D[0], D[3] - D[2], uvw[0]); +- +- return grad; +-} +- +-template +-__hostdev__ bool TrilinearSampler::zeroCrossing(const ValueT (&v)[2][2][2]) +-{ +- static_assert(is_floating_point::value, "TrilinearSampler::zeroCrossing requires a floating-point type"); +- const bool less = v[0][0][0] < ValueT(0); +- return (less ^ (v[0][0][1] < ValueT(0))) || +- (less ^ (v[0][1][1] < ValueT(0))) || +- (less ^ (v[0][1][0] < ValueT(0))) || +- (less ^ (v[1][0][0] < ValueT(0))) || +- (less ^ (v[1][0][1] < ValueT(0))) || +- (less ^ (v[1][1][1] < ValueT(0))) || +- (less ^ (v[1][1][0] < ValueT(0))); +-} +- +-/// @brief Template specialization that does not use caching of stencil points +-template +-class SampleFromVoxels : public TrilinearSampler +-{ +- using BaseT = TrilinearSampler; +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +- +-public: +- +- /// @brief Construction from a Tree or ReadAccessor +- __hostdev__ SampleFromVoxels(const TreeOrAccT& acc) : BaseT(acc) {} +- +- /// @note xyz is in index space space +- template class Vec3T> +- inline __hostdev__ ValueT operator()(Vec3T xyz) const; +- +- /// @note ijk is in index space space +- __hostdev__ ValueT operator()(const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);} +- +- /// @brief Return the gradient in index space. +- /// +- /// @warning Will only compile with floating point value types +- template class Vec3T> +- inline __hostdev__ Vec3T gradient(Vec3T xyz) const; +- +- /// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position. +- /// +- /// @warning Will only compile with floating point value types +- template class Vec3T> +- inline __hostdev__ bool zeroCrossing(Vec3T xyz) const; +- +-}; // SampleFromVoxels +- +-/// @brief Template specialization with caching of stencil values +-template +-class SampleFromVoxels : public TrilinearSampler +-{ +- using BaseT = TrilinearSampler; +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +- +- mutable CoordT mPos; +- mutable ValueT mVal[2][2][2]; +- +- template class Vec3T> +- __hostdev__ void cache(Vec3T& xyz) const; +-public: +- +- /// @brief Construction from a Tree or ReadAccessor +- __hostdev__ SampleFromVoxels(const TreeOrAccT& acc) : BaseT(acc), mPos(CoordT::max()){} +- +- /// @note xyz is in index space space +- template class Vec3T> +- inline __hostdev__ ValueT operator()(Vec3T xyz) const; +- +- // @note ijk is in index space space +- __hostdev__ ValueT operator()(const CoordT &ijk) const; +- +- /// @brief Return the gradient in index space. +- /// +- /// @warning Will only compile with floating point value types +- template class Vec3T> +- inline __hostdev__ Vec3T gradient(Vec3T xyz) const; +- +- /// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position. +- /// +- /// @warning Will only compile with floating point value types +- template class Vec3T> +- inline __hostdev__ bool zeroCrossing(Vec3T xyz) const; +- +- /// @brief Return true if the cached tri-linear stencil has a zero crossing. +- /// +- /// @warning Will only compile with floating point value types +- __hostdev__ bool zeroCrossing() const { return BaseT::zeroCrossing(mVal); } +- +-}; // SampleFromVoxels +- +-template +-template class Vec3T> +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const +-{ +- this->cache(xyz); +- return BaseT::sample(xyz, mVal); +-} +- +-template +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(const CoordT &ijk) const +-{ +- return ijk == mPos ? mVal[0][0][0] : BaseT::mAcc.getValue(ijk); +-} +- +-template +-template class Vec3T> +-__hostdev__ Vec3T SampleFromVoxels::gradient(Vec3T xyz) const +-{ +- this->cache(xyz); +- return BaseT::gradient(xyz, mVal); +-} +- +-template +-template class Vec3T> +-__hostdev__ bool SampleFromVoxels::zeroCrossing(Vec3T xyz) const +-{ +- this->cache(xyz); +- return BaseT::zeroCrossing(mVal); +-} +- +-template +-template class Vec3T> +-__hostdev__ void SampleFromVoxels::cache(Vec3T& xyz) const +-{ +- CoordT ijk = Floor(xyz); +- if (ijk != mPos) { +- mPos = ijk; +- BaseT::stencil(ijk, mVal); +- } +-} +- +-#if 0 +- +-template +-template class Vec3T> +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const +-{ +- ValueT val[2][2][2]; +- CoordT ijk = Floor(xyz); +- BaseT::stencil(ijk, val); +- return BaseT::sample(xyz, val); +-} +- +-#else +- +-template +-template class Vec3T> +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const +-{ +- auto lerp = [](ValueT a, ValueT b, RealT w) { return a + ValueT(w) * (b - a); }; +- +- CoordT coord = Floor(xyz); +- +- ValueT vx, vx1, vy, vy1, vz, vz1; +- +- vz = BaseT::mAcc.getValue(coord); +- coord[2] += 1; +- vz1 = BaseT::mAcc.getValue(coord); +- vy = lerp(vz, vz1, xyz[2]); +- +- coord[1] += 1; +- +- vz1 = BaseT::mAcc.getValue(coord); +- coord[2] -= 1; +- vz = BaseT::mAcc.getValue(coord); +- vy1 = lerp(vz, vz1, xyz[2]); +- +- vx = lerp(vy, vy1, xyz[1]); +- +- coord[0] += 1; +- +- vz = BaseT::mAcc.getValue(coord); +- coord[2] += 1; +- vz1 = BaseT::mAcc.getValue(coord); +- vy1 = lerp(vz, vz1, xyz[2]); +- +- coord[1] -= 1; +- +- vz1 = BaseT::mAcc.getValue(coord); +- coord[2] -= 1; +- vz = BaseT::mAcc.getValue(coord); +- vy = lerp(vz, vz1, xyz[2]); +- +- vx1 = lerp(vy, vy1, xyz[1]); +- +- return lerp(vx, vx1, xyz[0]); +-} +-#endif +- +- +-template +-template class Vec3T> +-__hostdev__ inline Vec3T SampleFromVoxels::gradient(Vec3T xyz) const +-{ +- ValueT val[2][2][2]; +- CoordT ijk = Floor(xyz); +- BaseT::stencil(ijk, val); +- return BaseT::gradient(xyz, val); +-} +- +-template +-template class Vec3T> +-__hostdev__ bool SampleFromVoxels::zeroCrossing(Vec3T xyz) const +-{ +- ValueT val[2][2][2]; +- CoordT ijk = Floor(xyz); +- BaseT::stencil(ijk, val); +- return BaseT::zeroCrossing(val); +-} +- +-// ------------------------------> TriquadraticSampler <-------------------------------------- +- +-/// @brief Tri-quadratic sampler, i.e. second order, interpolator +-template +-class TriquadraticSampler +-{ +-protected: +- const TreeOrAccT& mAcc; +- +-public: +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +- static const int ORDER = 1; +- +- /// @brief Protected constructor from a Tree or ReadAccessor +- __hostdev__ TriquadraticSampler(const TreeOrAccT& acc) : mAcc(acc) {} +- +- __hostdev__ const TreeOrAccT& accessor() const { return mAcc; } +- +- /// @brief Extract the stencil of 27 values +- inline __hostdev__ void stencil(const CoordT &ijk, ValueT (&v)[3][3][3]) const; +- +- template class Vec3T> +- static inline __hostdev__ ValueT sample(const Vec3T &uvw, const ValueT (&v)[3][3][3]); +- +- static inline __hostdev__ bool zeroCrossing(const ValueT (&v)[3][3][3]); +-}; // TriquadraticSamplerBase +- +-template +-__hostdev__ void TriquadraticSampler::stencil(const CoordT &ijk, ValueT (&v)[3][3][3]) const +-{ +- CoordT p(ijk[0] - 1, 0, 0); +- for (int dx = 0; dx < 3; ++dx, ++p[0]) { +- p[1] = ijk[1] - 1; +- for (int dy = 0; dy < 3; ++dy, ++p[1]) { +- p[2] = ijk[2] - 1; +- for (int dz = 0; dz < 3; ++dz, ++p[2]) { +- v[dx][dy][dz] = mAcc.getValue(p);// extract the stencil of 27 values +- } +- } +- } +-} +- +-template +-template class Vec3T> +-__hostdev__ typename TreeOrAccT::ValueType TriquadraticSampler::sample(const Vec3T &uvw, const ValueT (&v)[3][3][3]) +-{ +- auto kernel = [](const ValueT* value, double weight)->ValueT { +- return weight * (weight * (0.5f * (value[0] + value[2]) - value[1]) + +- 0.5f * (value[2] - value[0])) + value[1]; +- }; +- +- ValueT vx[3]; +- for (int dx = 0; dx < 3; ++dx) { +- ValueT vy[3]; +- for (int dy = 0; dy < 3; ++dy) { +- vy[dy] = kernel(&v[dx][dy][0], uvw[2]); +- }//loop over y +- vx[dx] = kernel(vy, uvw[1]); +- }//loop over x +- return kernel(vx, uvw[0]); +-} +- +-template +-__hostdev__ bool TriquadraticSampler::zeroCrossing(const ValueT (&v)[3][3][3]) +-{ +- static_assert(is_floating_point::value, "TrilinearSampler::zeroCrossing requires a floating-point type"); +- const bool less = v[0][0][0] < ValueT(0); +- for (int dx = 0; dx < 3; ++dx) { +- for (int dy = 0; dy < 3; ++dy) { +- for (int dz = 0; dz < 3; ++dz) { +- if (less ^ (v[dx][dy][dz] < ValueT(0))) return true; +- } +- } +- } +- return false; +-} +- +-/// @brief Template specialization that does not use caching of stencil points +-template +-class SampleFromVoxels : public TriquadraticSampler +-{ +- using BaseT = TriquadraticSampler; +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +-public: +- +- /// @brief Construction from a Tree or ReadAccessor +- __hostdev__ SampleFromVoxels(const TreeOrAccT& acc) : BaseT(acc) {} +- +- /// @note xyz is in index space space +- template class Vec3T> +- inline __hostdev__ ValueT operator()(Vec3T xyz) const; +- +- __hostdev__ ValueT operator()(const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);} +- +- /// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position. +- /// +- /// @warning Will only compile with floating point value types +- template class Vec3T> +- inline __hostdev__ bool zeroCrossing(Vec3T xyz) const; +- +-}; // SampleFromVoxels +- +-/// @brief Template specialization with caching of stencil values +-template +-class SampleFromVoxels : public TriquadraticSampler +-{ +- using BaseT = TriquadraticSampler; +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +- +- mutable CoordT mPos; +- mutable ValueT mVal[3][3][3]; +- +- template class Vec3T> +- __hostdev__ void cache(Vec3T& xyz) const; +-public: +- +- /// @brief Construction from a Tree or ReadAccessor +- __hostdev__ SampleFromVoxels(const TreeOrAccT& acc) : BaseT(acc), mPos(CoordT::max()){} +- +- /// @note xyz is in index space space +- template class Vec3T> +- inline __hostdev__ ValueT operator()(Vec3T xyz) const; +- +- inline __hostdev__ ValueT operator()(const CoordT &ijk) const; +- +- /// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position. +- /// +- /// @warning Will only compile with floating point value types +- template class Vec3T> +- inline __hostdev__ bool zeroCrossing(Vec3T xyz) const; +- +- /// @brief Return true if the cached tri-linear stencil has a zero crossing. +- /// +- /// @warning Will only compile with floating point value types +- __hostdev__ bool zeroCrossing() const { return BaseT::zeroCrossing(mVal); } +- +-}; // SampleFromVoxels +- +-template +-template class Vec3T> +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const +-{ +- this->cache(xyz); +- return BaseT::sample(xyz, mVal); +-} +- +-template +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(const CoordT &ijk) const +-{ +- return ijk == mPos ? mVal[1][1][1] : BaseT::mAcc.getValue(ijk); +-} +- +-template +-template class Vec3T> +-__hostdev__ bool SampleFromVoxels::zeroCrossing(Vec3T xyz) const +-{ +- this->cache(xyz); +- return BaseT::zeroCrossing(mVal); +-} +- +-template +-template class Vec3T> +-__hostdev__ void SampleFromVoxels::cache(Vec3T& xyz) const +-{ +- CoordT ijk = Floor(xyz); +- if (ijk != mPos) { +- mPos = ijk; +- BaseT::stencil(ijk, mVal); +- } +-} +- +-template +-template class Vec3T> +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const +-{ +- ValueT val[3][3][3]; +- CoordT ijk = Floor(xyz); +- BaseT::stencil(ijk, val); +- return BaseT::sample(xyz, val); +-} +- +-template +-template class Vec3T> +-__hostdev__ bool SampleFromVoxels::zeroCrossing(Vec3T xyz) const +-{ +- ValueT val[3][3][3]; +- CoordT ijk = Floor(xyz); +- BaseT::stencil(ijk, val); +- return BaseT::zeroCrossing(val); +-} +- +-// ------------------------------> TricubicSampler <-------------------------------------- +- +-/// @brief Tri-cubic sampler, i.e. third order, interpolator. +-/// +-/// @details See the following paper for implementation details: +-/// Lekien, F. and Marsden, J.: Tricubic interpolation in three dimensions. +-/// In: International Journal for Numerical Methods +-/// in Engineering (2005), No. 63, p. 455-471 +- +-template +-class TricubicSampler +-{ +-protected: +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +- +- const TreeOrAccT& mAcc; +- +-public: +- /// @brief Construction from a Tree or ReadAccessor +- __hostdev__ TricubicSampler(const TreeOrAccT& acc) +- : mAcc(acc) +- { +- } +- +- __hostdev__ const TreeOrAccT& accessor() const { return mAcc; } +- +- /// @brief Extract the stencil of 8 values +- inline __hostdev__ void stencil(const CoordT& ijk, ValueT (&c)[64]) const; +- +- template class Vec3T> +- static inline __hostdev__ ValueT sample(const Vec3T &uvw, const ValueT (&c)[64]); +-}; // TricubicSampler +- +-template +-__hostdev__ void TricubicSampler::stencil(const CoordT& ijk, ValueT (&C)[64]) const +-{ +- auto fetch = [&](int i, int j, int k) -> ValueT& { return C[((i + 1) << 4) + ((j + 1) << 2) + k + 1]; }; +- +- // fetch 64 point stencil values +- for (int i = -1; i < 3; ++i) { +- for (int j = -1; j < 3; ++j) { +- fetch(i, j, -1) = mAcc.getValue(ijk + CoordT(i, j, -1)); +- fetch(i, j, 0) = mAcc.getValue(ijk + CoordT(i, j, 0)); +- fetch(i, j, 1) = mAcc.getValue(ijk + CoordT(i, j, 1)); +- fetch(i, j, 2) = mAcc.getValue(ijk + CoordT(i, j, 2)); +- } +- } +- const ValueT half(0.5), quarter(0.25), eighth(0.125); +- const ValueT X[64] = {// values of f(x,y,z) at the 8 corners (each from 1 stencil value). +- fetch(0, 0, 0), +- fetch(1, 0, 0), +- fetch(0, 1, 0), +- fetch(1, 1, 0), +- fetch(0, 0, 1), +- fetch(1, 0, 1), +- fetch(0, 1, 1), +- fetch(1, 1, 1), +- // values of df/dx at the 8 corners (each from 2 stencil values). +- half * (fetch(1, 0, 0) - fetch(-1, 0, 0)), +- half * (fetch(2, 0, 0) - fetch(0, 0, 0)), +- half * (fetch(1, 1, 0) - fetch(-1, 1, 0)), +- half * (fetch(2, 1, 0) - fetch(0, 1, 0)), +- half * (fetch(1, 0, 1) - fetch(-1, 0, 1)), +- half * (fetch(2, 0, 1) - fetch(0, 0, 1)), +- half * (fetch(1, 1, 1) - fetch(-1, 1, 1)), +- half * (fetch(2, 1, 1) - fetch(0, 1, 1)), +- // values of df/dy at the 8 corners (each from 2 stencil values). +- half * (fetch(0, 1, 0) - fetch(0, -1, 0)), +- half * (fetch(1, 1, 0) - fetch(1, -1, 0)), +- half * (fetch(0, 2, 0) - fetch(0, 0, 0)), +- half * (fetch(1, 2, 0) - fetch(1, 0, 0)), +- half * (fetch(0, 1, 1) - fetch(0, -1, 1)), +- half * (fetch(1, 1, 1) - fetch(1, -1, 1)), +- half * (fetch(0, 2, 1) - fetch(0, 0, 1)), +- half * (fetch(1, 2, 1) - fetch(1, 0, 1)), +- // values of df/dz at the 8 corners (each from 2 stencil values). +- half * (fetch(0, 0, 1) - fetch(0, 0, -1)), +- half * (fetch(1, 0, 1) - fetch(1, 0, -1)), +- half * (fetch(0, 1, 1) - fetch(0, 1, -1)), +- half * (fetch(1, 1, 1) - fetch(1, 1, -1)), +- half * (fetch(0, 0, 2) - fetch(0, 0, 0)), +- half * (fetch(1, 0, 2) - fetch(1, 0, 0)), +- half * (fetch(0, 1, 2) - fetch(0, 1, 0)), +- half * (fetch(1, 1, 2) - fetch(1, 1, 0)), +- // values of d2f/dxdy at the 8 corners (each from 4 stencil values). +- quarter * (fetch(1, 1, 0) - fetch(-1, 1, 0) - fetch(1, -1, 0) + fetch(-1, -1, 0)), +- quarter * (fetch(2, 1, 0) - fetch(0, 1, 0) - fetch(2, -1, 0) + fetch(0, -1, 0)), +- quarter * (fetch(1, 2, 0) - fetch(-1, 2, 0) - fetch(1, 0, 0) + fetch(-1, 0, 0)), +- quarter * (fetch(2, 2, 0) - fetch(0, 2, 0) - fetch(2, 0, 0) + fetch(0, 0, 0)), +- quarter * (fetch(1, 1, 1) - fetch(-1, 1, 1) - fetch(1, -1, 1) + fetch(-1, -1, 1)), +- quarter * (fetch(2, 1, 1) - fetch(0, 1, 1) - fetch(2, -1, 1) + fetch(0, -1, 1)), +- quarter * (fetch(1, 2, 1) - fetch(-1, 2, 1) - fetch(1, 0, 1) + fetch(-1, 0, 1)), +- quarter * (fetch(2, 2, 1) - fetch(0, 2, 1) - fetch(2, 0, 1) + fetch(0, 0, 1)), +- // values of d2f/dxdz at the 8 corners (each from 4 stencil values). +- quarter * (fetch(1, 0, 1) - fetch(-1, 0, 1) - fetch(1, 0, -1) + fetch(-1, 0, -1)), +- quarter * (fetch(2, 0, 1) - fetch(0, 0, 1) - fetch(2, 0, -1) + fetch(0, 0, -1)), +- quarter * (fetch(1, 1, 1) - fetch(-1, 1, 1) - fetch(1, 1, -1) + fetch(-1, 1, -1)), +- quarter * (fetch(2, 1, 1) - fetch(0, 1, 1) - fetch(2, 1, -1) + fetch(0, 1, -1)), +- quarter * (fetch(1, 0, 2) - fetch(-1, 0, 2) - fetch(1, 0, 0) + fetch(-1, 0, 0)), +- quarter * (fetch(2, 0, 2) - fetch(0, 0, 2) - fetch(2, 0, 0) + fetch(0, 0, 0)), +- quarter * (fetch(1, 1, 2) - fetch(-1, 1, 2) - fetch(1, 1, 0) + fetch(-1, 1, 0)), +- quarter * (fetch(2, 1, 2) - fetch(0, 1, 2) - fetch(2, 1, 0) + fetch(0, 1, 0)), +- // values of d2f/dydz at the 8 corners (each from 4 stencil values). +- quarter * (fetch(0, 1, 1) - fetch(0, -1, 1) - fetch(0, 1, -1) + fetch(0, -1, -1)), +- quarter * (fetch(1, 1, 1) - fetch(1, -1, 1) - fetch(1, 1, -1) + fetch(1, -1, -1)), +- quarter * (fetch(0, 2, 1) - fetch(0, 0, 1) - fetch(0, 2, -1) + fetch(0, 0, -1)), +- quarter * (fetch(1, 2, 1) - fetch(1, 0, 1) - fetch(1, 2, -1) + fetch(1, 0, -1)), +- quarter * (fetch(0, 1, 2) - fetch(0, -1, 2) - fetch(0, 1, 0) + fetch(0, -1, 0)), +- quarter * (fetch(1, 1, 2) - fetch(1, -1, 2) - fetch(1, 1, 0) + fetch(1, -1, 0)), +- quarter * (fetch(0, 2, 2) - fetch(0, 0, 2) - fetch(0, 2, 0) + fetch(0, 0, 0)), +- quarter * (fetch(1, 2, 2) - fetch(1, 0, 2) - fetch(1, 2, 0) + fetch(1, 0, 0)), +- // values of d3f/dxdydz at the 8 corners (each from 8 stencil values). +- eighth * (fetch(1, 1, 1) - fetch(-1, 1, 1) - fetch(1, -1, 1) + fetch(-1, -1, 1) - fetch(1, 1, -1) + fetch(-1, 1, -1) + fetch(1, -1, -1) - fetch(-1, -1, -1)), +- eighth * (fetch(2, 1, 1) - fetch(0, 1, 1) - fetch(2, -1, 1) + fetch(0, -1, 1) - fetch(2, 1, -1) + fetch(0, 1, -1) + fetch(2, -1, -1) - fetch(0, -1, -1)), +- eighth * (fetch(1, 2, 1) - fetch(-1, 2, 1) - fetch(1, 0, 1) + fetch(-1, 0, 1) - fetch(1, 2, -1) + fetch(-1, 2, -1) + fetch(1, 0, -1) - fetch(-1, 0, -1)), +- eighth * (fetch(2, 2, 1) - fetch(0, 2, 1) - fetch(2, 0, 1) + fetch(0, 0, 1) - fetch(2, 2, -1) + fetch(0, 2, -1) + fetch(2, 0, -1) - fetch(0, 0, -1)), +- eighth * (fetch(1, 1, 2) - fetch(-1, 1, 2) - fetch(1, -1, 2) + fetch(-1, -1, 2) - fetch(1, 1, 0) + fetch(-1, 1, 0) + fetch(1, -1, 0) - fetch(-1, -1, 0)), +- eighth * (fetch(2, 1, 2) - fetch(0, 1, 2) - fetch(2, -1, 2) + fetch(0, -1, 2) - fetch(2, 1, 0) + fetch(0, 1, 0) + fetch(2, -1, 0) - fetch(0, -1, 0)), +- eighth * (fetch(1, 2, 2) - fetch(-1, 2, 2) - fetch(1, 0, 2) + fetch(-1, 0, 2) - fetch(1, 2, 0) + fetch(-1, 2, 0) + fetch(1, 0, 0) - fetch(-1, 0, 0)), +- eighth * (fetch(2, 2, 2) - fetch(0, 2, 2) - fetch(2, 0, 2) + fetch(0, 0, 2) - fetch(2, 2, 0) + fetch(0, 2, 0) + fetch(2, 0, 0) - fetch(0, 0, 0))}; +- +- // 4Kb of static table (int8_t has a range of -127 -> 127 which suffices) +- static const int8_t A[64][64] = { +- {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {-3, 3, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {2, -2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {9, -9, -9, 9, 0, 0, 0, 0, 6, 3, -6, -3, 0, 0, 0, 0, 6, -6, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {-6, 6, 6, -6, 0, 0, 0, 0, -3, -3, 3, 3, 0, 0, 0, 0, -4, 4, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {-6, 6, 6, -6, 0, 0, 0, 0, -4, -2, 4, 2, 0, 0, 0, 0, -3, 3, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {4, -4, -4, 4, 0, 0, 0, 0, 2, 2, -2, -2, 0, 0, 0, 0, 2, -2, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, -9, -9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, -6, -3, 0, 0, 0, 0, 6, -6, 3, -3, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 3, 3, 0, 0, 0, 0, -4, 4, -2, 2, 0, 0, 0, 0, -2, -2, -1, -1, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -2, 4, 2, 0, 0, 0, 0, -3, 3, -3, 3, 0, 0, 0, 0, -2, -1, -2, -1, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, -2, -2, 0, 0, 0, 0, 2, -2, 2, -2, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0}, +- {-3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {9, -9, 0, 0, -9, 9, 0, 0, 6, 3, 0, 0, -6, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {-6, 6, 0, 0, 6, -6, 0, 0, -3, -3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, -9, 0, 0, -9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, 0, 0, -6, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 3, -3, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, -2, 2, 0, 0, -2, -2, 0, 0, -1, -1, 0, 0}, +- {9, 0, -9, 0, -9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, -6, 0, -3, 0, 6, 0, -6, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 9, 0, -9, 0, -9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, -6, 0, -3, 0, 6, 0, -6, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0}, +- {-27, 27, 27, -27, 27, -27, -27, 27, -18, -9, 18, 9, 18, 9, -18, -9, -18, 18, -9, 9, 18, -18, 9, -9, -18, 18, 18, -18, -9, 9, 9, -9, -12, -6, -6, -3, 12, 6, 6, 3, -12, -6, 12, 6, -6, -3, 6, 3, -12, 12, -6, 6, -6, 6, -3, 3, -8, -4, -4, -2, -4, -2, -2, -1}, +- {18, -18, -18, 18, -18, 18, 18, -18, 9, 9, -9, -9, -9, -9, 9, 9, 12, -12, 6, -6, -12, 12, -6, 6, 12, -12, -12, 12, 6, -6, -6, 6, 6, 6, 3, 3, -6, -6, -3, -3, 6, 6, -6, -6, 3, 3, -3, -3, 8, -8, 4, -4, 4, -4, 2, -2, 4, 4, 2, 2, 2, 2, 1, 1}, +- {-6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, -3, 0, 3, 0, 3, 0, -4, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, -3, 0, 3, 0, 3, 0, -4, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -1, 0, -1, 0}, +- {18, -18, -18, 18, -18, 18, 18, -18, 12, 6, -12, -6, -12, -6, 12, 6, 9, -9, 9, -9, -9, 9, -9, 9, 12, -12, -12, 12, 6, -6, -6, 6, 6, 3, 6, 3, -6, -3, -6, -3, 8, 4, -8, -4, 4, 2, -4, -2, 6, -6, 6, -6, 3, -3, 3, -3, 4, 2, 4, 2, 2, 1, 2, 1}, +- {-12, 12, 12, -12, 12, -12, -12, 12, -6, -6, 6, 6, 6, 6, -6, -6, -6, 6, -6, 6, 6, -6, 6, -6, -8, 8, 8, -8, -4, 4, 4, -4, -3, -3, -3, -3, 3, 3, 3, 3, -4, -4, 4, 4, -2, -2, 2, 2, -4, 4, -4, 4, -2, 2, -2, 2, -2, -2, -2, -2, -1, -1, -1, -1}, +- {2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {-6, 6, 0, 0, 6, -6, 0, 0, -4, -2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {4, -4, 0, 0, -4, 4, 0, 0, 2, 2, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -3, 3, 0, 0, -2, -1, 0, 0, -2, -1, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0}, +- {-6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, -2, 0, 4, 0, 2, 0, -3, 0, 3, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, -2, 0, 4, 0, 2, 0, -3, 0, 3, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, -2, 0, -1, 0}, +- {18, -18, -18, 18, -18, 18, 18, -18, 12, 6, -12, -6, -12, -6, 12, 6, 12, -12, 6, -6, -12, 12, -6, 6, 9, -9, -9, 9, 9, -9, -9, 9, 8, 4, 4, 2, -8, -4, -4, -2, 6, 3, -6, -3, 6, 3, -6, -3, 6, -6, 3, -3, 6, -6, 3, -3, 4, 2, 2, 1, 4, 2, 2, 1}, +- {-12, 12, 12, -12, 12, -12, -12, 12, -6, -6, 6, 6, 6, 6, -6, -6, -8, 8, -4, 4, 8, -8, 4, -4, -6, 6, 6, -6, -6, 6, 6, -6, -4, -4, -2, -2, 4, 4, 2, 2, -3, -3, 3, 3, -3, -3, 3, 3, -4, 4, -2, 2, -4, 4, -2, 2, -2, -2, -1, -1, -2, -2, -1, -1}, +- {4, 0, -4, 0, -4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, -2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +- {0, 0, 0, 0, 0, 0, 0, 0, 4, 0, -4, 0, -4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, -2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0}, +- {-12, 12, 12, -12, 12, -12, -12, 12, -8, -4, 8, 4, 8, 4, -8, -4, -6, 6, -6, 6, 6, -6, 6, -6, -6, 6, 6, -6, -6, 6, 6, -6, -4, -2, -4, -2, 4, 2, 4, 2, -4, -2, 4, 2, -4, -2, 4, 2, -3, 3, -3, 3, -3, 3, -3, 3, -2, -1, -2, -1, -2, -1, -2, -1}, +- {8, -8, -8, 8, -8, 8, 8, -8, 4, 4, -4, -4, -4, -4, 4, 4, 4, -4, 4, -4, -4, 4, -4, 4, 4, -4, -4, 4, 4, -4, -4, 4, 2, 2, 2, 2, -2, -2, -2, -2, 2, 2, -2, -2, 2, 2, -2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 1, 1, 1, 1, 1, 1, 1, 1}}; +- +- for (int i = 0; i < 64; ++i) { // C = A * X +- C[i] = ValueT(0); +-#if 0 +- for (int j = 0; j < 64; j += 4) { +- C[i] = fma(A[i][j], X[j], fma(A[i][j+1], X[j+1], fma(A[i][j+2], X[j+2], fma(A[i][j+3], X[j+3], C[i])))); +- } +-#else +- for (int j = 0; j < 64; j += 4) { +- C[i] += A[i][j] * X[j] + A[i][j + 1] * X[j + 1] + A[i][j + 2] * X[j + 2] + A[i][j + 3] * X[j + 3]; +- } +-#endif +- } +-} +- +-template +-template class Vec3T> +-__hostdev__ typename TreeOrAccT::ValueType TricubicSampler::sample(const Vec3T &xyz, const ValueT (&C)[64]) +-{ +- ValueT zPow(1), sum(0); +- for (int k = 0, n = 0; k < 4; ++k) { +- ValueT yPow(1); +- for (int j = 0; j < 4; ++j, n += 4) { +-#if 0 +- sum = fma( yPow, zPow * fma(xyz[0], fma(xyz[0], fma(xyz[0], C[n + 3], C[n + 2]), C[n + 1]), C[n]), sum); +-#else +- sum += yPow * zPow * (C[n] + xyz[0] * (C[n + 1] + xyz[0] * (C[n + 2] + xyz[0] * C[n + 3]))); +-#endif +- yPow *= xyz[1]; +- } +- zPow *= xyz[2]; +- } +- return sum; +-} +- +-template +-class SampleFromVoxels : public TricubicSampler +-{ +- using BaseT = TricubicSampler; +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +- +- mutable CoordT mPos; +- mutable ValueT mC[64]; +- +- template class Vec3T> +- __hostdev__ void cache(Vec3T& xyz) const; +- +-public: +- /// @brief Construction from a Tree or ReadAccessor +- __hostdev__ SampleFromVoxels(const TreeOrAccT& acc) +- : BaseT(acc) +- { +- } +- +- /// @note xyz is in index space space +- template class Vec3T> +- inline __hostdev__ ValueT operator()(Vec3T xyz) const; +- +- // @brief Return value at the coordinate @a ijk in index space space +- __hostdev__ ValueT operator()(const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);} +- +-}; // SampleFromVoxels +- +-template +-template class Vec3T> +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const +-{ +- this->cache(xyz); +- return BaseT::sample(xyz, mC); +-} +- +-template +-template class Vec3T> +-__hostdev__ void SampleFromVoxels::cache(Vec3T& xyz) const +-{ +- CoordT ijk = Floor(xyz); +- if (ijk != mPos) { +- mPos = ijk; +- BaseT::stencil(ijk, mC); +- } +-} +- +-template +-class SampleFromVoxels : public TricubicSampler +-{ +- using BaseT = TricubicSampler; +- using ValueT = typename TreeOrAccT::ValueType; +- using CoordT = typename TreeOrAccT::CoordType; +- +-public: +- /// @brief Construction from a Tree or ReadAccessor +- __hostdev__ SampleFromVoxels(const TreeOrAccT& acc) +- : BaseT(acc) +- { +- } +- +- /// @note xyz is in index space space +- template class Vec3T> +- inline __hostdev__ ValueT operator()(Vec3T xyz) const; +- +- __hostdev__ ValueT operator()(const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);} +- +-}; // SampleFromVoxels +- +-template +-template class Vec3T> +-__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const +-{ +- ValueT C[64]; +- CoordT ijk = Floor(xyz); +- BaseT::stencil(ijk, C); +- return BaseT::sample(xyz, C); +-} +- +-} // namespace nanovdb +- +-#endif // NANOVDB_SAMPLE_FROM_VOXELS_H_HAS_BEEN_INCLUDED ++// SampleFromVoxels.h ++// Copyright Contributors to the OpenVDB Project ++// SPDX-License-Identifier: MPL-2.0 ++ ++////////////////////////////////////////////////////////////////////////// ++/// ++/// @file SampleFromVoxels.h ++/// ++/// @brief NearestNeighborSampler, TrilinearSampler, TriquadraticSampler and TricubicSampler ++/// ++/// @note These interpolators employ internal caching for better performance when used repeatedly ++/// in the same voxel location, so try to reuse an instance of these classes more than once. ++/// ++/// @warning While all the interpolators defined below work with both scalars and vectors ++/// values (e.g. float and Vec3) TrilinarSampler::zeroCrossing and ++/// Trilinear::gradient will only compile with floating point value types. ++/// ++/// @author Ken Museth ++/// ++/////////////////////////////////////////////////////////////////////////// ++ ++#ifndef NANOVDB_SAMPLE_FROM_VOXELS_H_HAS_BEEN_INCLUDED ++#define NANOVDB_SAMPLE_FROM_VOXELS_H_HAS_BEEN_INCLUDED ++ ++// Only define __hostdev__ when compiling as NVIDIA CUDA ++#ifdef __CUDACC__ ++#define __hostdev__ __host__ __device__ ++#elif defined(__KERNEL_METAL__) ++#else ++#include // for floor ++#define __hostdev__ ++#endif ++ ++namespace nanovdb { ++ ++// Forward declaration of sampler with specific polynomial orders ++template ++class SampleFromVoxels; ++ ++/// @brief Factory free-function for a sampler of specific polynomial orders ++/// ++/// @details This allows for the compact syntax: ++/// @code ++/// auto acc = grid.getAccessor(); ++/// auto smp = nanovdb::createSampler<1>( acc ); ++/// @endcode ++template ++__hostdev__ SampleFromVoxels createSampler(__global__ const TreeOrAccT& acc) ++{ ++ return SampleFromVoxels(acc); ++} ++ ++/// @brief Utility function that returns the Coord of the round-down of @a xyz ++/// and redefined @xyz as the fractional part, ie xyz-in = return-value + xyz-out ++template class Vec3T> ++__hostdev__ inline CoordT Floor(__global__ Vec3T& xyz); ++ ++/// @brief Template specialization of Floor for Vec3 ++template class Vec3T> ++__hostdev__ inline CoordT Floor(__global__ Vec3T& xyz) ++{ ++ const float ijk[3] = {floorf(xyz[0]), floorf(xyz[1]), floorf(xyz[2])}; ++ xyz[0] -= ijk[0]; ++ xyz[1] -= ijk[1]; ++ xyz[2] -= ijk[2]; ++ return CoordT(int32_t(ijk[0]), int32_t(ijk[1]), int32_t(ijk[2])); ++} ++ ++/// @brief Template specialization of Floor for Vec3 ++template class Vec3T> ++__hostdev__ inline CoordT Floor(__global__ Vec3T& xyz) ++{ ++ const double ijk[3] = {floor(xyz[0]), floor(xyz[1]), floor(xyz[2])}; ++ xyz[0] -= ijk[0]; ++ xyz[1] -= ijk[1]; ++ xyz[2] -= ijk[2]; ++ return CoordT(int32_t(ijk[0]), int32_t(ijk[1]), int32_t(ijk[2])); ++} ++ ++#if defined(__KERNEL_METAL__) ++/// @brief Template specialization of Floor for Vec3 ++template class Vec3T> ++__hostdev__ inline CoordT Floor(__local__ Vec3T& xyz) ++{ ++ const float ijk[3] = {floorf(xyz[0]), floorf(xyz[1]), floorf(xyz[2])}; ++ xyz[0] -= ijk[0]; ++ xyz[1] -= ijk[1]; ++ xyz[2] -= ijk[2]; ++ return CoordT(int32_t(ijk[0]), int32_t(ijk[1]), int32_t(ijk[2])); ++} ++ ++/// @brief Template specialization of Floor for Vec3 ++template class Vec3T> ++__hostdev__ inline CoordT Floor(__local__ Vec3T& xyz) ++{ ++ const double ijk[3] = {floor(xyz[0]), floor(xyz[1]), floor(xyz[2])}; ++ xyz[0] -= ijk[0]; ++ xyz[1] -= ijk[1]; ++ xyz[2] -= ijk[2]; ++ return CoordT(int32_t(ijk[0]), int32_t(ijk[1]), int32_t(ijk[2])); ++} ++#endif ++ ++// ------------------------------> NearestNeighborSampler <-------------------------------------- ++ ++/// @brief Nearest neighbor, i.e. zero order, interpolator with caching ++template ++class SampleFromVoxels ++{ ++public: ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++ ++ static __constant__ const int ORDER = 0; ++ /// @brief Construction from a Tree or ReadAccessor ++ __hostdev__ SampleFromVoxels(__local__ const TreeOrAccT& acc) ++ : mAcc(acc) ++ , mPos(CoordT::max()) ++ { ++ } ++ ++ __hostdev__ __global__ const TreeOrAccT& accessor() const { return mAcc; } ++ ++ /// @note xyz is in index space space ++ template ++ inline __hostdev__ ValueT operator()(__global__ const Vec3T& xyz) const __local__; ++#if defined(__KERNEL_METAL__) ++ template ++ inline __hostdev__ ValueT operator()(__local__ const Vec3T& xyz) const __local__; ++#endif ++ ++ inline __hostdev__ ValueT operator()(__global__ const CoordT& ijk) const __local__; ++ ++ inline __hostdev__ ValueT operator()() const; ++ ++private: ++ __global__ const TreeOrAccT& mAcc; ++ mutable CoordT mPos; ++ mutable ValueT mVal; // private cache ++}; // SampleFromVoxels ++ ++/// @brief Nearest neighbor, i.e. zero order, interpolator without caching ++template ++class SampleFromVoxels ++{ ++public: ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++ static __constant__ const int ORDER = 0; ++ ++ /// @brief Construction from a Tree or ReadAccessor ++ __hostdev__ SampleFromVoxels(__local__ const TreeOrAccT& acc) ++ : mAcc(acc) ++ { ++ } ++ ++ __hostdev__ __global__ const TreeOrAccT& accessor() const __local__ { return mAcc; } ++ ++ /// @note xyz is in index space space ++ template ++ inline __hostdev__ ValueT operator()(__global__ const Vec3T& xyz) const __local__; ++#if defined(__KERNEL_METAL__) ++ template ++ inline __hostdev__ ValueT operator()(__local__ const Vec3T& xyz) const __local__; ++#endif ++ ++ inline __hostdev__ ValueT operator()(__global__ const CoordT& ijk) const __local__ { return mAcc.getValue(ijk);} ++ ++private: ++ __local__ const TreeOrAccT& mAcc; ++}; // SampleFromVoxels ++ ++template ++template ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(__global__ const Vec3T& xyz) const __local__ ++{ ++ const CoordT ijk = Round(xyz); ++ if (ijk != mPos) { ++ mPos = ijk; ++ mVal = mAcc.getValue(mPos); ++ } ++ return mVal; ++} ++#if defined(__KERNEL_METAL__) ++template ++template ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(__local__ const Vec3T& xyz) const __local__ ++{ ++ const CoordT ijk = Round(xyz); ++ if (ijk != mPos) { ++ mPos = ijk; ++ mVal = mAcc.getValue(mPos); ++ } ++ return mVal; ++} ++#endif ++ ++template ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(__global__ const CoordT& ijk) const __local__ ++{ ++ if (ijk != mPos) { ++ mPos = ijk; ++ mVal = mAcc.getValue(mPos); ++ } ++ return mVal; ++} ++ ++template ++template ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(__global__ const Vec3T& xyz) const __local__ ++{ ++ return mAcc.getValue(Round(xyz)); ++} ++ ++#if defined(__KERNEL_METAL__) ++template ++template ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(__local__ const Vec3T& xyz) const __local__ ++{ ++ return mAcc.getValue(Round(xyz)); ++} ++#endif ++ ++// ------------------------------> TrilinearSampler <-------------------------------------- ++ ++/// @brief Tri-linear sampler, i.e. first order, interpolator ++template ++class TrilinearSampler ++{ ++#if defined(__KERNEL_METAL__) ++public: ++#else ++protected: ++#endif ++ __local__ const TreeOrAccT& mAcc; ++ ++public: ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++ static __constant__ const int ORDER = 1; ++ ++ /// @brief Protected constructor from a Tree or ReadAccessor ++ __hostdev__ TrilinearSampler(__local__ const TreeOrAccT& acc) : mAcc(acc) {} ++ ++ __hostdev__ __global__ const TreeOrAccT& accessor() const { return mAcc; } ++ ++ /// @brief Extract the stencil of 8 values ++ inline __hostdev__ void stencil(__global__ CoordT& ijk, __global__ ValueT (&v)[2][2][2]) const; ++ ++ template class Vec3T> ++ static inline __hostdev__ ValueT sample(__global__ const Vec3T &uvw, __global__ const ValueT (&v)[2][2][2]); ++ ++ template class Vec3T> ++ static inline __hostdev__ Vec3T gradient(__global__ const Vec3T &uvw, __global__ const ValueT (&v)[2][2][2]); ++ ++ static inline __hostdev__ bool zeroCrossing(__global__ const ValueT (&v)[2][2][2]); ++}; // TrilinearSamplerBase ++ ++template ++void TrilinearSampler::stencil(__global__ CoordT& ijk, __global__ ValueT (&v)[2][2][2]) const ++{ ++ v[0][0][0] = mAcc.getValue(ijk); // i, j, k ++ ++ ijk[2] += 1; ++ v[0][0][1] = mAcc.getValue(ijk); // i, j, k + 1 ++ ++ ijk[1] += 1; ++ v[0][1][1] = mAcc.getValue(ijk); // i, j+1, k + 1 ++ ++ ijk[2] -= 1; ++ v[0][1][0] = mAcc.getValue(ijk); // i, j+1, k ++ ++ ijk[0] += 1; ++ ijk[1] -= 1; ++ v[1][0][0] = mAcc.getValue(ijk); // i+1, j, k ++ ++ ijk[2] += 1; ++ v[1][0][1] = mAcc.getValue(ijk); // i+1, j, k + 1 ++ ++ ijk[1] += 1; ++ v[1][1][1] = mAcc.getValue(ijk); // i+1, j+1, k + 1 ++ ++ ijk[2] -= 1; ++ v[1][1][0] = mAcc.getValue(ijk); // i+1, j+1, k ++} ++ ++template ++template class Vec3T> ++typename TreeOrAccT::ValueType TrilinearSampler::sample(__global__ const Vec3T &uvw, __global__ const ValueT (&v)[2][2][2]) ++{ ++#if 0 ++ auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b-a, a); };// = w*(b-a) + a ++ //auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b, fma(-w, a, a));};// = (1-w)*a + w*b ++#else ++ struct Lerp { ++ static ValueT lerp(ValueT a, ValueT b, RealT w) { return a + ValueT(w) * (b - a); } ++ }; ++#endif ++ return Lerp::lerp(Lerp::lerp(Lerp::lerp(v[0][0][0], v[0][0][1], uvw[2]), Lerp::lerp(v[0][1][0], v[0][1][1], uvw[2]), uvw[1]), ++ Lerp::lerp(Lerp::lerp(v[1][0][0], v[1][0][1], uvw[2]), Lerp::lerp(v[1][1][0], v[1][1][1], uvw[2]), uvw[1]), ++ uvw[0]); ++} ++ ++template ++template class Vec3T> ++Vec3T TrilinearSampler::gradient(__global__ const Vec3T &uvw, __global__ const ValueT (&v)[2][2][2]) ++{ ++ static_assert(is_floating_point::value, "TrilinearSampler::gradient requires a floating-point type"); ++#if 0 ++ auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b-a, a); };// = w*(b-a) + a ++ //auto lerp = [](ValueT a, ValueT b, ValueT w){ return fma(w, b, fma(-w, a, a));};// = (1-w)*a + w*b ++#else ++ struct Lerp { ++ static ValueT lerp(ValueT a, ValueT b, RealT w) { return a + ValueT(w) * (b - a); } ++ }; ++#endif ++ ++ ValueT D[4] = {v[0][0][1] - v[0][0][0], v[0][1][1] - v[0][1][0], v[1][0][1] - v[1][0][0], v[1][1][1] - v[1][1][0]}; ++ ++ // Z component ++ Vec3T grad(0, 0, Lerp::lerp(Lerp::lerp(D[0], D[1], uvw[1]), lerp(D[2], D[3], uvw[1]), uvw[0])); ++ ++ const ValueT w = ValueT(uvw[2]); ++ D[0] = v[0][0][0] + D[0] * w; ++ D[1] = v[0][1][0] + D[1] * w; ++ D[2] = v[1][0][0] + D[2] * w; ++ D[3] = v[1][1][0] + D[3] * w; ++ ++ // X component ++ grad[0] = Lerp::lerp(D[2], D[3], uvw[1]) - Lerp::lerp(D[0], D[1], uvw[1]); ++ ++ // Y component ++ grad[1] = Lerp::lerp(D[1] - D[0], D[3] - D[2], uvw[0]); ++ ++ return grad; ++} ++ ++template ++bool TrilinearSampler::zeroCrossing(__global__ const ValueT (&v)[2][2][2]) ++{ ++ static_assert(is_floating_point::value, "TrilinearSampler::zeroCrossing requires a floating-point type"); ++ const bool less = v[0][0][0] < ValueT(0); ++ return (less ^ (v[0][0][1] < ValueT(0))) || ++ (less ^ (v[0][1][1] < ValueT(0))) || ++ (less ^ (v[0][1][0] < ValueT(0))) || ++ (less ^ (v[1][0][0] < ValueT(0))) || ++ (less ^ (v[1][0][1] < ValueT(0))) || ++ (less ^ (v[1][1][1] < ValueT(0))) || ++ (less ^ (v[1][1][0] < ValueT(0))); ++} ++ ++/// @brief Template specialization that does not use caching of stencil points ++template ++class SampleFromVoxels ++#if !defined(__KERNEL_METAL__) ++ : public TrilinearSampler ++#endif ++{ ++#if defined(__KERNEL_METAL__) ++ ++ TrilinearSampler _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) BaseT::v ++ ++#endif ++ using BaseT = TrilinearSampler; ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++ ++public: ++ ++ /// @brief Construction from a Tree or ReadAccessor ++#if defined(__KERNEL_METAL__) ++ __hostdev__ SampleFromVoxels(__local__ const TreeOrAccT& acc) : _base(acc) {} ++#else ++ __hostdev__ SampleFromVoxels(__local__ const TreeOrAccT& acc) : BaseT(acc) {} ++#endif ++ ++ /// @note xyz is in index space space ++ template class Vec3T> ++ inline __hostdev__ ValueT operator()(Vec3T xyz) const; ++ ++ /// @note ijk is in index space space ++ __hostdev__ ValueT operator()(__global__ const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);} ++ ++ /// @brief Return the gradient in index space. ++ /// ++ /// @warning Will only compile with floating point value types ++ template class Vec3T> ++ inline __hostdev__ Vec3T gradient(Vec3T xyz) const; ++ ++ /// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position. ++ /// ++ /// @warning Will only compile with floating point value types ++ template class Vec3T> ++ inline __hostdev__ bool zeroCrossing(Vec3T xyz) const; ++ ++}; // SampleFromVoxels ++ ++/// @brief Template specialization with caching of stencil values ++template ++class SampleFromVoxels ++#if !defined(__KERNEL_METAL__) ++ : public TrilinearSampler ++#endif ++{ ++#if defined(__KERNEL_METAL__) ++ TrilinearSampler _base; ++#endif ++ using BaseT = TrilinearSampler; ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++ ++ mutable CoordT mPos; ++ mutable ValueT mVal[2][2][2]; ++ ++ template class Vec3T> ++ __hostdev__ void cache(__global__ Vec3T& xyz) const; ++public: ++ ++ /// @brief Construction from a Tree or ReadAccessor ++ __hostdev__ SampleFromVoxels(__local__ const TreeOrAccT& acc) : BaseT(acc), mPos(CoordT::max()){} ++ ++ /// @note xyz is in index space space ++ template class Vec3T> ++ inline __hostdev__ ValueT operator()(Vec3T xyz) const; ++ ++ // @note ijk is in index space space ++ __hostdev__ ValueT operator()(__global__ const CoordT &ijk) const; ++ ++ /// @brief Return the gradient in index space. ++ /// ++ /// @warning Will only compile with floating point value types ++ template class Vec3T> ++ inline __hostdev__ Vec3T gradient(Vec3T xyz) const; ++ ++ /// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position. ++ /// ++ /// @warning Will only compile with floating point value types ++ template class Vec3T> ++ inline __hostdev__ bool zeroCrossing(Vec3T xyz) const; ++ ++ /// @brief Return true if the cached tri-linear stencil has a zero crossing. ++ /// ++ /// @warning Will only compile with floating point value types ++ __hostdev__ bool zeroCrossing() const { return BaseT::zeroCrossing(mVal); } ++ ++}; // SampleFromVoxels ++ ++template ++template class Vec3T> ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const ++{ ++ this->cache(xyz); ++ return BaseT::sample(xyz, mVal); ++} ++ ++template ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(__global__ const CoordT &ijk) const ++{ ++ return ijk == mPos ? mVal[0][0][0] : BaseT::mAcc.getValue(ijk); ++} ++ ++template ++template class Vec3T> ++Vec3T SampleFromVoxels::gradient(Vec3T xyz) const ++{ ++ this->cache(xyz); ++ return BaseT::gradient(xyz, mVal); ++} ++ ++template ++template class Vec3T> ++__hostdev__ bool SampleFromVoxels::zeroCrossing(Vec3T xyz) const ++{ ++ this->cache(xyz); ++ return BaseT::zeroCrossing(mVal); ++} ++ ++template ++template class Vec3T> ++void SampleFromVoxels::cache(__global__ Vec3T& xyz) const ++{ ++ CoordT ijk = Floor(xyz); ++ if (ijk != mPos) { ++ mPos = ijk; ++ BaseT::stencil(ijk, mVal); ++ } ++} ++ ++#if 0 ++ ++template ++template class Vec3T> ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const ++{ ++ ValueT val[2][2][2]; ++ CoordT ijk = Floor(xyz); ++ BaseT::stencil(ijk, val); ++ return BaseT::sample(xyz, val); ++} ++ ++#else ++ ++template ++template class Vec3T> ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const ++{ ++ struct Lerp { ++ static ValueT lerp(ValueT a, ValueT b, RealT w) { return a + ValueT(w) * (b - a); } ++ }; ++ ++ CoordT coord = Floor(xyz); ++ ++ ValueT vx, vx1, vy, vy1, vz, vz1; ++ ++ vz = BASE(mAcc).getValue(coord); ++ coord[2] += 1; ++ vz1 = BASE(mAcc).getValue(coord); ++ vy = Lerp::lerp(vz, vz1, xyz[2]); ++ ++ coord[1] += 1; ++ ++ vz1 = BASE(mAcc).getValue(coord); ++ coord[2] -= 1; ++ vz = BASE(mAcc).getValue(coord); ++ vy1 = Lerp::lerp(vz, vz1, xyz[2]); ++ ++ vx = Lerp::lerp(vy, vy1, xyz[1]); ++ ++ coord[0] += 1; ++ ++ vz = BASE(mAcc).getValue(coord); ++ coord[2] += 1; ++ vz1 = BASE(mAcc).getValue(coord); ++ vy1 = Lerp::lerp(vz, vz1, xyz[2]); ++ ++ coord[1] -= 1; ++ ++ vz1 = BASE(mAcc).getValue(coord); ++ coord[2] -= 1; ++ vz = BASE(mAcc).getValue(coord); ++ vy = Lerp::lerp(vz, vz1, xyz[2]); ++ ++ vx1 = Lerp::lerp(vy, vy1, xyz[1]); ++ ++ return Lerp::lerp(vx, vx1, xyz[0]); ++} ++#endif ++ ++ ++template ++template class Vec3T> ++inline Vec3T SampleFromVoxels::gradient(Vec3T xyz) const ++{ ++ ValueT val[2][2][2]; ++ CoordT ijk = Floor(xyz); ++ BaseT::stencil(ijk, val); ++ return BaseT::gradient(xyz, val); ++} ++ ++template ++template class Vec3T> ++bool SampleFromVoxels::zeroCrossing(Vec3T xyz) const ++{ ++ ValueT val[2][2][2]; ++ CoordT ijk = Floor(xyz); ++ BaseT::stencil(ijk, val); ++ return BaseT::zeroCrossing(val); ++} ++ ++// ------------------------------> TriquadraticSampler <-------------------------------------- ++ ++/// @brief Tri-quadratic sampler, i.e. second order, interpolator ++template ++class TriquadraticSampler ++{ ++protected: ++ __local__ const TreeOrAccT& mAcc; ++ ++public: ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++ static __constant__ const int ORDER = 1; ++ ++ /// @brief Protected constructor from a Tree or ReadAccessor ++ __hostdev__ TriquadraticSampler(__local__ const TreeOrAccT& acc) : mAcc(acc) {} ++ ++ __hostdev__ __global__ const TreeOrAccT& accessor() const { return mAcc; } ++ ++ /// @brief Extract the stencil of 27 values ++ inline __hostdev__ void stencil(__local__ const CoordT &ijk, __local__ ValueT (&v)[3][3][3]) const; ++ ++ template class Vec3T> ++ static inline __hostdev__ ValueT sample(__local__ const Vec3T &uvw, __local__ const ValueT (&v)[3][3][3]); ++ ++ static inline __hostdev__ bool zeroCrossing(__global__ const ValueT (&v)[3][3][3]); ++}; // TriquadraticSamplerBase ++ ++template ++void TriquadraticSampler::stencil(__local__ const CoordT &ijk, __local__ ValueT (&v)[3][3][3]) const ++{ ++ CoordT p(ijk[0] - 1, 0, 0); ++ for (int dx = 0; dx < 3; ++dx, ++p[0]) { ++ p[1] = ijk[1] - 1; ++ for (int dy = 0; dy < 3; ++dy, ++p[1]) { ++ p[2] = ijk[2] - 1; ++ for (int dz = 0; dz < 3; ++dz, ++p[2]) { ++ v[dx][dy][dz] = mAcc.getValue(p);// extract the stencil of 27 values ++ } ++ } ++ } ++} ++ ++template ++template class Vec3T> ++typename TreeOrAccT::ValueType TriquadraticSampler::sample(__local__ const Vec3T &uvw, __local__ const ValueT (&v)[3][3][3]) ++{ ++ struct Kernel { ++ static ValueT _kernel(__local__ const ValueT* value, double weight) { ++ return weight * (weight * (0.5f * (value[0] + value[2]) - value[1]) + 0.5f * (value[2] - value[0])) + value[1]; ++ } ++ }; ++ ++ ValueT vx[3]; ++ for (int dx = 0; dx < 3; ++dx) { ++ ValueT vy[3]; ++ for (int dy = 0; dy < 3; ++dy) { ++ vy[dy] = Kernel::_kernel(&v[dx][dy][0], uvw[2]); ++ }//loop over y ++ vx[dx] = Kernel::_kernel(vy, uvw[1]); ++ }//loop over x ++ return Kernel::_kernel(vx, uvw[0]); ++} ++ ++template ++bool TriquadraticSampler::zeroCrossing(__global__ const ValueT (&v)[3][3][3]) ++{ ++ static_assert(is_floating_point::value, "TrilinearSampler::zeroCrossing requires a floating-point type"); ++ const bool less = v[0][0][0] < ValueT(0); ++ for (int dx = 0; dx < 3; ++dx) { ++ for (int dy = 0; dy < 3; ++dy) { ++ for (int dz = 0; dz < 3; ++dz) { ++ if (less ^ (v[dx][dy][dz] < ValueT(0))) return true; ++ } ++ } ++ } ++ return false; ++} ++ ++/// @brief Template specialization that does not use caching of stencil points ++template ++class SampleFromVoxels ++#if !defined(__KERNEL_METAL__) ++ : public TriquadraticSampler ++#endif ++{ ++#if defined(__KERNEL_METAL__) ++ TriquadraticSampler _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) BaseT::v ++#endif ++ using BaseT = TriquadraticSampler; ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++public: ++ ++ /// @brief Construction from a Tree or ReadAccessor ++#if defined(__KERNEL_METAL__) ++ __hostdev__ SampleFromVoxels(__local__ const TreeOrAccT& acc) : _base(acc) {} ++#else ++ __hostdev__ SampleFromVoxels(__local__ const TreeOrAccT& acc) : BaseT(acc) {} ++#endif ++ ++ /// @note xyz is in index space space ++ template class Vec3T> ++ inline __hostdev__ ValueT operator()(Vec3T xyz) const; ++ ++ __hostdev__ ValueT operator()(__global__ const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);} ++ ++ /// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position. ++ /// ++ /// @warning Will only compile with floating point value types ++ template class Vec3T> ++ inline __hostdev__ bool zeroCrossing(Vec3T xyz) const; ++ ++}; // SampleFromVoxels ++ ++/// @brief Template specialization with caching of stencil values ++template ++class SampleFromVoxels ++#if !defined(__KERNEL_METAL__) ++ : public TriquadraticSampler ++#endif ++{ ++#if defined(__KERNEL_METAL__) ++ TriquadraticSampler _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) BaseT::v ++#endif ++ using BaseT = TriquadraticSampler; ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++ ++ mutable CoordT mPos; ++ mutable ValueT mVal[3][3][3]; ++ ++ template class Vec3T> ++ __hostdev__ void cache(__global__ Vec3T& xyz) const; ++public: ++ ++ /// @brief Construction from a Tree or ReadAccessor ++ __hostdev__ SampleFromVoxels(__local__ const TreeOrAccT& acc) : BaseT(acc), mPos(CoordT::max()){} ++ ++ /// @note xyz is in index space space ++ template class Vec3T> ++ inline __hostdev__ ValueT operator()(Vec3T xyz) const; ++ ++ inline __hostdev__ ValueT operator()(__global__ const CoordT &ijk) const; ++ ++ /// @brief Return true if the tr-linear stencil has a zero crossing at the specified index position. ++ /// ++ /// @warning Will only compile with floating point value types ++ template class Vec3T> ++ inline __hostdev__ bool zeroCrossing(Vec3T xyz) const; ++ ++ /// @brief Return true if the cached tri-linear stencil has a zero crossing. ++ /// ++ /// @warning Will only compile with floating point value types ++ __hostdev__ bool zeroCrossing() const { return BaseT::zeroCrossing(mVal); } ++ ++}; // SampleFromVoxels ++ ++template ++template class Vec3T> ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const ++{ ++ this->cache(xyz); ++ return BaseT::sample(xyz, mVal); ++} ++ ++template ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(__global__ const CoordT &ijk) const ++{ ++ return ijk == mPos ? mVal[1][1][1] : BaseT::mAcc.getValue(ijk); ++} ++ ++template ++template class Vec3T> ++__hostdev__ bool SampleFromVoxels::zeroCrossing(Vec3T xyz) const ++{ ++ this->cache(xyz); ++ return BaseT::zeroCrossing(mVal); ++} ++ ++template ++template class Vec3T> ++void SampleFromVoxels::cache(__global__ Vec3T& xyz) const ++{ ++ CoordT ijk = Floor(xyz); ++ if (ijk != mPos) { ++ mPos = ijk; ++ BaseT::stencil(ijk, mVal); ++ } ++} ++ ++template ++template class Vec3T> ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const ++{ ++ ValueT val[3][3][3]; ++ CoordT ijk = Floor(xyz); ++ BASE(stencil)(ijk, val); ++ return BaseT::sample(xyz, val); ++} ++ ++template ++template class Vec3T> ++bool SampleFromVoxels::zeroCrossing(Vec3T xyz) const ++{ ++ ValueT val[3][3][3]; ++ CoordT ijk = Floor(xyz); ++ BaseT::stencil(ijk, val); ++ return BaseT::zeroCrossing(val); ++} ++ ++// ------------------------------> TricubicSampler <-------------------------------------- ++ ++/// @brief Tri-cubic sampler, i.e. third order, interpolator. ++/// ++/// @details See the following paper for implementation details: ++/// Lekien, F. and Marsden, J.: Tricubic interpolation in three dimensions. ++/// In: International Journal for Numerical Methods ++/// in Engineering (2005), No. 63, p. 455-471 ++ ++template ++class TricubicSampler ++{ ++protected: ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++ ++ __global__ const TreeOrAccT& mAcc; ++ ++public: ++ /// @brief Construction from a Tree or ReadAccessor ++ __hostdev__ TricubicSampler(__global__ const TreeOrAccT& acc) ++ : mAcc(acc) ++ { ++ } ++ ++ __hostdev__ __global__ const TreeOrAccT& accessor() const { return mAcc; } ++ ++ /// @brief Extract the stencil of 8 values ++ inline __hostdev__ void stencil(__global__ const CoordT& ijk, __global__ ValueT (&c)[64]) const; ++ ++ template class Vec3T> ++ static inline __hostdev__ ValueT sample(__global__ const Vec3T &uvw, __global__ const ValueT (&c)[64]); ++}; // TricubicSampler ++ ++// 4Kb of static table (int8_t has a range of -127 -> 127 which suffices) ++static __constant__ const int8_t TricubicSampler_A[64][64] = { ++ {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {-3, 3, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {2, -2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {9, -9, -9, 9, 0, 0, 0, 0, 6, 3, -6, -3, 0, 0, 0, 0, 6, -6, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {-6, 6, 6, -6, 0, 0, 0, 0, -3, -3, 3, 3, 0, 0, 0, 0, -4, 4, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {-6, 6, 6, -6, 0, 0, 0, 0, -4, -2, 4, 2, 0, 0, 0, 0, -3, 3, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {4, -4, -4, 4, 0, 0, 0, 0, 2, 2, -2, -2, 0, 0, 0, 0, 2, -2, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, -9, -9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, -6, -3, 0, 0, 0, 0, 6, -6, 3, -3, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 3, 3, 0, 0, 0, 0, -4, 4, -2, 2, 0, 0, 0, 0, -2, -2, -1, -1, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -2, 4, 2, 0, 0, 0, 0, -3, 3, -3, 3, 0, 0, 0, 0, -2, -1, -2, -1, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, -2, -2, 0, 0, 0, 0, 2, -2, 2, -2, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0}, ++ {-3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {9, -9, 0, 0, -9, 9, 0, 0, 6, 3, 0, 0, -6, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {-6, 6, 0, 0, 6, -6, 0, 0, -3, -3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, -9, 0, 0, -9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, 0, 0, -6, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 3, -3, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, -2, 2, 0, 0, -2, -2, 0, 0, -1, -1, 0, 0}, ++ {9, 0, -9, 0, -9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, -6, 0, -3, 0, 6, 0, -6, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 9, 0, -9, 0, -9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, -6, 0, -3, 0, 6, 0, -6, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0}, ++ {-27, 27, 27, -27, 27, -27, -27, 27, -18, -9, 18, 9, 18, 9, -18, -9, -18, 18, -9, 9, 18, -18, 9, -9, -18, 18, 18, -18, -9, 9, 9, -9, -12, -6, -6, -3, 12, 6, 6, 3, -12, -6, 12, 6, -6, -3, 6, 3, -12, 12, -6, 6, -6, 6, -3, 3, -8, -4, -4, -2, -4, -2, -2, -1}, ++ {18, -18, -18, 18, -18, 18, 18, -18, 9, 9, -9, -9, -9, -9, 9, 9, 12, -12, 6, -6, -12, 12, -6, 6, 12, -12, -12, 12, 6, -6, -6, 6, 6, 6, 3, 3, -6, -6, -3, -3, 6, 6, -6, -6, 3, 3, -3, -3, 8, -8, 4, -4, 4, -4, 2, -2, 4, 4, 2, 2, 2, 2, 1, 1}, ++ {-6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, -3, 0, 3, 0, 3, 0, -4, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, -3, 0, 3, 0, 3, 0, -4, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -1, 0, -1, 0}, ++ {18, -18, -18, 18, -18, 18, 18, -18, 12, 6, -12, -6, -12, -6, 12, 6, 9, -9, 9, -9, -9, 9, -9, 9, 12, -12, -12, 12, 6, -6, -6, 6, 6, 3, 6, 3, -6, -3, -6, -3, 8, 4, -8, -4, 4, 2, -4, -2, 6, -6, 6, -6, 3, -3, 3, -3, 4, 2, 4, 2, 2, 1, 2, 1}, ++ {-12, 12, 12, -12, 12, -12, -12, 12, -6, -6, 6, 6, 6, 6, -6, -6, -6, 6, -6, 6, 6, -6, 6, -6, -8, 8, 8, -8, -4, 4, 4, -4, -3, -3, -3, -3, 3, 3, 3, 3, -4, -4, 4, 4, -2, -2, 2, 2, -4, 4, -4, 4, -2, 2, -2, 2, -2, -2, -2, -2, -1, -1, -1, -1}, ++ {2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {-6, 6, 0, 0, 6, -6, 0, 0, -4, -2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {4, -4, 0, 0, -4, 4, 0, 0, 2, 2, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -3, 3, 0, 0, -2, -1, 0, 0, -2, -1, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0}, ++ {-6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, -2, 0, 4, 0, 2, 0, -3, 0, 3, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, -2, 0, 4, 0, 2, 0, -3, 0, 3, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, -2, 0, -1, 0}, ++ {18, -18, -18, 18, -18, 18, 18, -18, 12, 6, -12, -6, -12, -6, 12, 6, 12, -12, 6, -6, -12, 12, -6, 6, 9, -9, -9, 9, 9, -9, -9, 9, 8, 4, 4, 2, -8, -4, -4, -2, 6, 3, -6, -3, 6, 3, -6, -3, 6, -6, 3, -3, 6, -6, 3, -3, 4, 2, 2, 1, 4, 2, 2, 1}, ++ {-12, 12, 12, -12, 12, -12, -12, 12, -6, -6, 6, 6, 6, 6, -6, -6, -8, 8, -4, 4, 8, -8, 4, -4, -6, 6, 6, -6, -6, 6, 6, -6, -4, -4, -2, -2, 4, 4, 2, 2, -3, -3, 3, 3, -3, -3, 3, 3, -4, 4, -2, 2, -4, 4, -2, 2, -2, -2, -1, -1, -2, -2, -1, -1}, ++ {4, 0, -4, 0, -4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, -2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ {0, 0, 0, 0, 0, 0, 0, 0, 4, 0, -4, 0, -4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, -2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0}, ++ {-12, 12, 12, -12, 12, -12, -12, 12, -8, -4, 8, 4, 8, 4, -8, -4, -6, 6, -6, 6, 6, -6, 6, -6, -6, 6, 6, -6, -6, 6, 6, -6, -4, -2, -4, -2, 4, 2, 4, 2, -4, -2, 4, 2, -4, -2, 4, 2, -3, 3, -3, 3, -3, 3, -3, 3, -2, -1, -2, -1, -2, -1, -2, -1}, ++ {8, -8, -8, 8, -8, 8, 8, -8, 4, 4, -4, -4, -4, -4, 4, 4, 4, -4, 4, -4, -4, 4, -4, 4, 4, -4, -4, 4, 4, -4, -4, 4, 2, 2, 2, 2, -2, -2, -2, -2, 2, 2, -2, -2, 2, 2, -2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 1, 1, 1, 1, 1, 1, 1, 1}}; ++ ++template ++void TricubicSampler::stencil(__global__ const CoordT& ijk, __global__ ValueT (&C)[64]) const ++{ ++ struct Fetch { ++ Fetch(__global__ ValueT (&_C)[64]):C(_C) {} ++ __global__ ValueT& fetch(int i, int j, int k) { return C[((i + 1) << 4) + ((j + 1) << 2) + k + 1]; } ++ ++ __global__ ValueT (&C)[64]; ++ }; ++ Fetch f(C); ++ ++ // fetch 64 point stencil values ++ for (int i = -1; i < 3; ++i) { ++ for (int j = -1; j < 3; ++j) { ++ Fetch::fetch(i, j, -1) = mAcc.getValue(ijk + CoordT(i, j, -1)); ++ Fetch::fetch(i, j, 0) = mAcc.getValue(ijk + CoordT(i, j, 0)); ++ Fetch::fetch(i, j, 1) = mAcc.getValue(ijk + CoordT(i, j, 1)); ++ Fetch::fetch(i, j, 2) = mAcc.getValue(ijk + CoordT(i, j, 2)); ++ } ++ } ++ const ValueT _half(0.5), quarter(0.25), eighth(0.125); ++ const ValueT X[64] = {// values of f(x,y,z) at the 8 corners (each from 1 stencil value). ++ f.fetch(0, 0, 0), ++ f.fetch(1, 0, 0), ++ f.fetch(0, 1, 0), ++ f.fetch(1, 1, 0), ++ f.fetch(0, 0, 1), ++ f.fetch(1, 0, 1), ++ f.fetch(0, 1, 1), ++ f.fetch(1, 1, 1), ++ // values of df/dx at the 8 corners (each from 2 stencil values). ++ _half * (f.fetch(1, 0, 0) - f.fetch(-1, 0, 0)), ++ _half * (f.fetch(2, 0, 0) - f.fetch(0, 0, 0)), ++ _half * (f.fetch(1, 1, 0) - f.fetch(-1, 1, 0)), ++ _half * (f.fetch(2, 1, 0) - f.fetch(0, 1, 0)), ++ _half * (f.fetch(1, 0, 1) - f.fetch(-1, 0, 1)), ++ _half * (f.fetch(2, 0, 1) - f.fetch(0, 0, 1)), ++ _half * (f.fetch(1, 1, 1) - f.fetch(-1, 1, 1)), ++ _half * (f.fetch(2, 1, 1) - f.fetch(0, 1, 1)), ++ // values of df/dy at the 8 corners (each from 2 stencil values). ++ _half * (f.fetch(0, 1, 0) - f.fetch(0, -1, 0)), ++ _half * (f.fetch(1, 1, 0) - f.fetch(1, -1, 0)), ++ _half * (f.fetch(0, 2, 0) - f.fetch(0, 0, 0)), ++ _half * (f.fetch(1, 2, 0) - f.fetch(1, 0, 0)), ++ _half * (f.fetch(0, 1, 1) - f.fetch(0, -1, 1)), ++ _half * (f.fetch(1, 1, 1) - f.fetch(1, -1, 1)), ++ _half * (f.fetch(0, 2, 1) - f.fetch(0, 0, 1)), ++ _half * (f.fetch(1, 2, 1) - f.fetch(1, 0, 1)), ++ // values of df/dz at the 8 corners (each from 2 stencil values). ++ _half * (f.fetch(0, 0, 1) - f.fetch(0, 0, -1)), ++ _half * (f.fetch(1, 0, 1) - f.fetch(1, 0, -1)), ++ _half * (f.fetch(0, 1, 1) - f.fetch(0, 1, -1)), ++ _half * (f.fetch(1, 1, 1) - f.fetch(1, 1, -1)), ++ _half * (f.fetch(0, 0, 2) - f.fetch(0, 0, 0)), ++ _half * (f.fetch(1, 0, 2) - f.fetch(1, 0, 0)), ++ _half * (f.fetch(0, 1, 2) - f.fetch(0, 1, 0)), ++ _half * (f.fetch(1, 1, 2) - f.fetch(1, 1, 0)), ++ // values of d2f/dxdy at the 8 corners (each from 4 stencil values). ++ quarter * (f.fetch(1, 1, 0) - f.fetch(-1, 1, 0) - f.fetch(1, -1, 0) + f.fetch(-1, -1, 0)), ++ quarter * (f.fetch(2, 1, 0) - f.fetch(0, 1, 0) - f.fetch(2, -1, 0) + f.fetch(0, -1, 0)), ++ quarter * (f.fetch(1, 2, 0) - f.fetch(-1, 2, 0) - f.fetch(1, 0, 0) + f.fetch(-1, 0, 0)), ++ quarter * (f.fetch(2, 2, 0) - f.fetch(0, 2, 0) - f.fetch(2, 0, 0) + f.fetch(0, 0, 0)), ++ quarter * (f.fetch(1, 1, 1) - f.fetch(-1, 1, 1) - f.fetch(1, -1, 1) + f.fetch(-1, -1, 1)), ++ quarter * (f.fetch(2, 1, 1) - f.fetch(0, 1, 1) - f.fetch(2, -1, 1) + f.fetch(0, -1, 1)), ++ quarter * (f.fetch(1, 2, 1) - f.fetch(-1, 2, 1) - f.fetch(1, 0, 1) + f.fetch(-1, 0, 1)), ++ quarter * (f.fetch(2, 2, 1) - f.fetch(0, 2, 1) - f.fetch(2, 0, 1) + f.fetch(0, 0, 1)), ++ // values of d2f/dxdz at the 8 corners (each from 4 stencil values). ++ quarter * (f.fetch(1, 0, 1) - f.fetch(-1, 0, 1) - f.fetch(1, 0, -1) + f.fetch(-1, 0, -1)), ++ quarter * (f.fetch(2, 0, 1) - f.fetch(0, 0, 1) - f.fetch(2, 0, -1) + f.fetch(0, 0, -1)), ++ quarter * (f.fetch(1, 1, 1) - f.fetch(-1, 1, 1) - f.fetch(1, 1, -1) + f.fetch(-1, 1, -1)), ++ quarter * (f.fetch(2, 1, 1) - f.fetch(0, 1, 1) - f.fetch(2, 1, -1) + f.fetch(0, 1, -1)), ++ quarter * (f.fetch(1, 0, 2) - f.fetch(-1, 0, 2) - f.fetch(1, 0, 0) + f.fetch(-1, 0, 0)), ++ quarter * (f.fetch(2, 0, 2) - f.fetch(0, 0, 2) - f.fetch(2, 0, 0) + f.fetch(0, 0, 0)), ++ quarter * (f.fetch(1, 1, 2) - f.fetch(-1, 1, 2) - f.fetch(1, 1, 0) + f.fetch(-1, 1, 0)), ++ quarter * (f.fetch(2, 1, 2) - f.fetch(0, 1, 2) - f.fetch(2, 1, 0) + f.fetch(0, 1, 0)), ++ // values of d2f/dydz at the 8 corners (each from 4 stencil values). ++ quarter * (f.fetch(0, 1, 1) - f.fetch(0, -1, 1) - f.fetch(0, 1, -1) + f.fetch(0, -1, -1)), ++ quarter * (f.fetch(1, 1, 1) - f.fetch(1, -1, 1) - f.fetch(1, 1, -1) + f.fetch(1, -1, -1)), ++ quarter * (f.fetch(0, 2, 1) - f.fetch(0, 0, 1) - f.fetch(0, 2, -1) + f.fetch(0, 0, -1)), ++ quarter * (f.fetch(1, 2, 1) - f.fetch(1, 0, 1) - f.fetch(1, 2, -1) + f.fetch(1, 0, -1)), ++ quarter * (f.fetch(0, 1, 2) - f.fetch(0, -1, 2) - f.fetch(0, 1, 0) + f.fetch(0, -1, 0)), ++ quarter * (f.fetch(1, 1, 2) - f.fetch(1, -1, 2) - f.fetch(1, 1, 0) + f.fetch(1, -1, 0)), ++ quarter * (f.fetch(0, 2, 2) - f.fetch(0, 0, 2) - f.fetch(0, 2, 0) + f.fetch(0, 0, 0)), ++ quarter * (f.fetch(1, 2, 2) - f.fetch(1, 0, 2) - f.fetch(1, 2, 0) + f.fetch(1, 0, 0)), ++ // values of d3f/dxdydz at the 8 corners (each from 8 stencil values). ++ eighth * (f.fetch(1, 1, 1) - f.fetch(-1, 1, 1) - f.fetch(1, -1, 1) + f.fetch(-1, -1, 1) - f.fetch(1, 1, -1) + f.fetch(-1, 1, -1) + f.fetch(1, -1, -1) - f.fetch(-1, -1, -1)), ++ eighth * (f.fetch(2, 1, 1) - f.fetch(0, 1, 1) - f.fetch(2, -1, 1) + f.fetch(0, -1, 1) - f.fetch(2, 1, -1) + f.fetch(0, 1, -1) + f.fetch(2, -1, -1) - f.fetch(0, -1, -1)), ++ eighth * (f.fetch(1, 2, 1) - f.fetch(-1, 2, 1) - f.fetch(1, 0, 1) + f.fetch(-1, 0, 1) - f.fetch(1, 2, -1) + f.fetch(-1, 2, -1) + f.fetch(1, 0, -1) - f.fetch(-1, 0, -1)), ++ eighth * (f.fetch(2, 2, 1) - f.fetch(0, 2, 1) - f.fetch(2, 0, 1) + f.fetch(0, 0, 1) - f.fetch(2, 2, -1) + f.fetch(0, 2, -1) + f.fetch(2, 0, -1) - f.fetch(0, 0, -1)), ++ eighth * (f.fetch(1, 1, 2) - f.fetch(-1, 1, 2) - f.fetch(1, -1, 2) + f.fetch(-1, -1, 2) - f.fetch(1, 1, 0) + f.fetch(-1, 1, 0) + f.fetch(1, -1, 0) - f.fetch(-1, -1, 0)), ++ eighth * (f.fetch(2, 1, 2) - f.fetch(0, 1, 2) - f.fetch(2, -1, 2) + f.fetch(0, -1, 2) - f.fetch(2, 1, 0) + f.fetch(0, 1, 0) + f.fetch(2, -1, 0) - f.fetch(0, -1, 0)), ++ eighth * (f.fetch(1, 2, 2) - f.fetch(-1, 2, 2) - f.fetch(1, 0, 2) + f.fetch(-1, 0, 2) - f.fetch(1, 2, 0) + f.fetch(-1, 2, 0) + f.fetch(1, 0, 0) - f.fetch(-1, 0, 0)), ++ eighth * (f.fetch(2, 2, 2) - f.fetch(0, 2, 2) - f.fetch(2, 0, 2) + f.fetch(0, 0, 2) - f.fetch(2, 2, 0) + f.fetch(0, 2, 0) + f.fetch(2, 0, 0) - f.fetch(0, 0, 0))}; ++ ++ for (int i = 0; i < 64; ++i) { // C = A * X ++ C[i] = ValueT(0); ++#if 0 ++ for (int j = 0; j < 64; j += 4) { ++ C[i] = fma(A[i][j], X[j], fma(A[i][j+1], X[j+1], fma(A[i][j+2], X[j+2], fma(A[i][j+3], X[j+3], C[i])))); ++ } ++#else ++ for (int j = 0; j < 64; j += 4) { ++ C[i] += TricubicSampler_A[i][j] * X[j] + TricubicSampler_A[i][j + 1] * X[j + 1] + ++ TricubicSampler_A[i][j + 2] * X[j + 2] + TricubicSampler_A[i][j + 3] * X[j + 3]; ++ } ++#endif ++ } ++} ++ ++template ++template class Vec3T> ++__hostdev__ typename TreeOrAccT::ValueType TricubicSampler::sample(__global__ const Vec3T &xyz, __global__ const ValueT (&C)[64]) ++{ ++ ValueT zPow(1), sum(0); ++ for (int k = 0, n = 0; k < 4; ++k) { ++ ValueT yPow(1); ++ for (int j = 0; j < 4; ++j, n += 4) { ++#if 0 ++ sum = fma( yPow, zPow * fma(xyz[0], fma(xyz[0], fma(xyz[0], C[n + 3], C[n + 2]), C[n + 1]), C[n]), sum); ++#else ++ sum += yPow * zPow * (C[n] + xyz[0] * (C[n + 1] + xyz[0] * (C[n + 2] + xyz[0] * C[n + 3]))); ++#endif ++ yPow *= xyz[1]; ++ } ++ zPow *= xyz[2]; ++ } ++ return sum; ++} ++ ++template ++class SampleFromVoxels ++#if !defined(__KERNEL_METAL__) ++ : public TricubicSampler ++#endif ++{ ++#if defined(__KERNEL_METAL__) ++ TricubicSampler _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) BaseT::v ++#endif ++ using BaseT = TricubicSampler; ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++ ++ mutable CoordT mPos; ++ mutable ValueT mC[64]; ++ ++ template class Vec3T> ++ __hostdev__ void cache(__global__ Vec3T& xyz) const; ++ ++public: ++ /// @brief Construction from a Tree or ReadAccessor ++ __hostdev__ SampleFromVoxels(__local__ const TreeOrAccT& acc) ++ : BaseT(acc) ++ { ++ } ++ ++ /// @note xyz is in index space space ++ template class Vec3T> ++ inline __hostdev__ ValueT operator()(Vec3T xyz) const; ++ ++ // @brief Return value at the coordinate @a ijk in index space space ++ __hostdev__ ValueT operator()(__global__ const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);} ++ ++}; // SampleFromVoxels ++ ++template ++template class Vec3T> ++typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const ++{ ++ this->cache(xyz); ++ return BaseT::sample(xyz, mC); ++} ++ ++template ++template class Vec3T> ++void SampleFromVoxels::cache(__global__ Vec3T& xyz) const ++{ ++ CoordT ijk = Floor(xyz); ++ if (ijk != mPos) { ++ mPos = ijk; ++ BaseT::stencil(ijk, mC); ++ } ++} ++ ++template ++class SampleFromVoxels ++#if !defined(__KERNEL_METAL__) ++ : public TricubicSampler ++#endif ++{ ++#if defined(__KERNEL_METAL__) ++ TricubicSampler _base; ++#define BASE(v) _base.v ++#else ++#define BASE(v) BaseT::v ++#endif ++ using BaseT = TricubicSampler; ++ using ValueT = typename TreeOrAccT::ValueType; ++ using CoordT = typename TreeOrAccT::CoordType; ++ ++public: ++ /// @brief Construction from a Tree or ReadAccessor ++ __hostdev__ SampleFromVoxels(__local__ const TreeOrAccT& acc) ++ : BaseT(acc) ++ { ++ } ++ ++ /// @note xyz is in index space space ++ template class Vec3T> ++ inline __hostdev__ ValueT operator()(Vec3T xyz) const; ++ ++ __hostdev__ ValueT operator()(__global__ const CoordT &ijk) const {return BaseT::mAcc.getValue(ijk);} ++ ++}; // SampleFromVoxels ++ ++template ++template class Vec3T> ++__hostdev__ typename TreeOrAccT::ValueType SampleFromVoxels::operator()(Vec3T xyz) const ++{ ++ ValueT C[64]; ++ CoordT ijk = Floor(xyz); ++ BaseT::stencil(ijk, C); ++ return BaseT::sample(xyz, C); ++} ++ ++} // namespace nanovdb ++ ++#endif // NANOVDB_SAMPLE_FROM_VOXELS_H_HAS_BEEN_INCLUDED diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake index 08cb882ebfb..10805728574 100644 --- a/build_files/cmake/config/blender_release.cmake +++ b/build_files/cmake/config/blender_release.cmake @@ -85,7 +85,7 @@ if(NOT APPLE) set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE) set(WITH_CYCLES_CUDA_BINARIES ON CACHE BOOL "" FORCE) set(WITH_CYCLES_CUBIN_COMPILER OFF CACHE BOOL "" FORCE) - set(WITH_CYCLES_HIP_BINARIES ON CACHE BOOL "" FORCE) + set(WITH_CYCLES_HIP_BINARIES OFF CACHE BOOL "" FORCE) set(WITH_CYCLES_DEVICE_ONEAPI ON CACHE BOOL "" FORCE) set(WITH_CYCLES_ONEAPI_BINARIES ON CACHE BOOL "" FORCE) endif() diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index fe3bb9d4737..958c8205bfd 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -1090,7 +1090,7 @@ function(msgfmt_simple add_custom_command( OUTPUT ${_file_to} COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_to_path} - COMMAND "$" ${_file_from} ${_file_to} + COMMAND ${CMAKE_COMMAND} -E env ${PLATFORM_ENV_BUILD} "$" ${_file_from} ${_file_to} DEPENDS msgfmt ${_file_from}) set_source_files_properties(${_file_to} PROPERTIES GENERATED TRUE) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 085255d03a6..2ee8697d695 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -1040,7 +1040,7 @@ endif() # Environment variables to run precompiled executables that needed libraries. list(JOIN PLATFORM_BUNDLED_LIBRARY_DIRS ";" _library_paths) -set(PLATFORM_ENV_BUILD_DIRS "${LIBDIR}/OpenImageIO/bin\;${LIBDIR}/boost/lib\;${LIBDIR}/openexr/bin\;${LIBDIR}/imath/bin\;${PATH}") +set(PLATFORM_ENV_BUILD_DIRS "${LIBDIR}/tbb/bin\;${LIBDIR}/OpenImageIO/bin\;${LIBDIR}/boost/lib\;${LIBDIR}/openexr/bin\;${LIBDIR}/imath/bin\;${PATH}") set(PLATFORM_ENV_BUILD "PATH=${PLATFORM_ENV_BUILD_DIRS}") # Install needs the additional folders from PLATFORM_ENV_BUILD_DIRS as well, as tools like idiff and abcls use the release mode dlls set(PLATFORM_ENV_INSTALL "PATH=${CMAKE_INSTALL_PREFIX_WITH_CONFIG}/blender.shared/\;${PLATFORM_ENV_BUILD_DIRS}\;$ENV{PATH}") diff --git a/build_files/cmake/project_info.py b/build_files/cmake/project_info.py index 12f92235b2a..401dab442e0 100755 --- a/build_files/cmake/project_info.py +++ b/build_files/cmake/project_info.py @@ -142,7 +142,7 @@ def cmake_advanced_info() -> Union[Tuple[List[str], List[Tuple[str, str]]], Tupl make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM") if make_exe is None: - print("Make command not found in: %r not found" % project_path) + print("Make command not found: CMAKE_MAKE_PROGRAM") return None, None make_exe_basename = os.path.basename(make_exe) diff --git a/build_files/utils/make_update.py b/build_files/utils/make_update.py index 076a6cdf782..e4dd13e36d9 100755 --- a/build_files/utils/make_update.py +++ b/build_files/utils/make_update.py @@ -66,9 +66,11 @@ def get_blender_git_root() -> str: # Setup for precompiled libraries and tests from svn. -def get_effective_architecture(args: argparse.Namespace): - if args.architecture: - return args.architecture +def get_effective_architecture(args: argparse.Namespace) -> str: + architecture = args.architecture + if architecture: + assert isinstance(architecture, str) + return architecture # Check platform.version to detect arm64 with x86_64 python binary. if "ARM64" in platform.version(): @@ -474,7 +476,7 @@ if __name__ == "__main__": major = blender_version.version // 100 minor = blender_version.version % 100 branch = f"blender-v{major}.{minor}-release" - release_version = f"{major}.{minor}" + release_version: Optional[str] = f"{major}.{minor}" else: branch = 'main' release_version = None diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index e1d8ac39bcf..a9fa740805d 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = V3.5 +PROJECT_NUMBER = V3.6 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 41eacb7417f..78543258349 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1676,17 +1676,20 @@ class CyclesPreferences(bpy.types.AddonPreferences): col.label(text="and NVIDIA driver version %s or newer" % driver_version, icon='BLANK1', translate=False) elif device_type == 'HIP': - import sys - if sys.platform[:3] == "win": - driver_version = "21.Q4" - col.label(text="Requires AMD GPU with Vega or RDNA architecture", icon='BLANK1') - col.label(text=iface_("and AMD Radeon Pro %s driver or newer") % driver_version, - icon='BLANK1', translate=False) - elif sys.platform.startswith("linux"): - driver_version = "22.10" - col.label(text="Requires AMD GPU with Vega or RDNA architecture", icon='BLANK1') - col.label(text=iface_("and AMD driver version %s or newer") % driver_version, icon='BLANK1', - translate=False) + if True: + col.label(text="HIP temporarily disabled due to compiler bugs", icon='BLANK1') + else: + import sys + if sys.platform[:3] == "win": + driver_version = "21.Q4" + col.label(text="Requires AMD GPU with Vega or RDNA architecture", icon='BLANK1') + col.label(text=iface_("and AMD Radeon Pro %s driver or newer") % driver_version, + icon='BLANK1', translate=False) + elif sys.platform.startswith("linux"): + driver_version = "22.10" + col.label(text="Requires AMD GPU with Vega or RDNA architecture", icon='BLANK1') + col.label(text=iface_("and AMD driver version %s or newer") % driver_version, icon='BLANK1', + translate=False) elif device_type == 'ONEAPI': import sys if sys.platform.startswith("win"): diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake index 4b165e1a5be..b10aeec8bdf 100644 --- a/intern/cycles/cmake/external_libs.cmake +++ b/intern/cycles/cmake/external_libs.cmake @@ -42,12 +42,15 @@ endif() ########################################################################### if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP) - find_package(HIP) - set_and_warn_library_found("HIP compiler" HIP_FOUND WITH_CYCLES_HIP_BINARIES) + set(WITH_CYCLES_HIP_BINARIES OFF) + message(STATUS "HIP temporarily disabled due to compiler bugs") - if(HIP_FOUND) - message(STATUS "Found HIP ${HIP_HIPCC_EXECUTABLE} (${HIP_VERSION})") - endif() + # find_package(HIP) + # set_and_warn_library_found("HIP compiler" HIP_FOUND WITH_CYCLES_HIP_BINARIES) + + # if(HIP_FOUND) + # message(STATUS "Found HIP ${HIP_HIPCC_EXECUTABLE} (${HIP_VERSION})") + # endif() endif() if(NOT WITH_HIP_DYNLOAD) diff --git a/intern/cycles/device/metal/device.mm b/intern/cycles/device/metal/device.mm index 5ffd3a09d56..b55ecf146cc 100644 --- a/intern/cycles/device/metal/device.mm +++ b/intern/cycles/device/metal/device.mm @@ -55,9 +55,8 @@ void device_metal_info(vector &devices) info.denoisers = DENOISER_NONE; info.id = id; - if (MetalInfo::get_device_vendor(device) == METAL_GPU_AMD) { - info.has_light_tree = false; - } + info.has_nanovdb = MetalInfo::get_device_vendor(device) == METAL_GPU_APPLE; + info.has_light_tree = MetalInfo::get_device_vendor(device) != METAL_GPU_AMD; devices.push_back(info); device_index++; diff --git a/intern/cycles/device/metal/device_impl.h b/intern/cycles/device/metal/device_impl.h index e698e00e80f..633618d0169 100644 --- a/intern/cycles/device/metal/device_impl.h +++ b/intern/cycles/device/metal/device_impl.h @@ -68,9 +68,12 @@ class MetalDevice : public Device { std::recursive_mutex metal_mem_map_mutex; /* Bindless Textures */ + bool is_texture(const TextureInfo &tex); device_vector texture_info; bool need_texture_info; id mtlTextureArgEncoder = nil; + id mtlBufferArgEncoder = nil; + id buffer_bindings_1d = nil; id texture_bindings_2d = nil; id texture_bindings_3d = nil; std::vector> texture_slot_map; diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index 5afb531c4ec..36d7fcf316d 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -91,11 +91,6 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile } } - texture_bindings_2d = [mtlDevice newBufferWithLength:4096 options:default_storage_mode]; - texture_bindings_3d = [mtlDevice newBufferWithLength:4096 options:default_storage_mode]; - - stats.mem_alloc(texture_bindings_2d.allocatedSize + texture_bindings_3d.allocatedSize); - switch (device_vendor) { default: break; @@ -156,6 +151,16 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile arg_desc_texture.dataType = MTLDataTypeTexture; arg_desc_texture.access = MTLArgumentAccessReadOnly; mtlTextureArgEncoder = [mtlDevice newArgumentEncoderWithArguments:@[ arg_desc_texture ]]; + MTLArgumentDescriptor *arg_desc_buffer = [[MTLArgumentDescriptor alloc] init]; + arg_desc_buffer.dataType = MTLDataTypePointer; + arg_desc_buffer.access = MTLArgumentAccessReadOnly; + mtlBufferArgEncoder = [mtlDevice newArgumentEncoderWithArguments:@[ arg_desc_buffer ]]; + + buffer_bindings_1d = [mtlDevice newBufferWithLength:8192 options:default_storage_mode]; + texture_bindings_2d = [mtlDevice newBufferWithLength:8192 options:default_storage_mode]; + texture_bindings_3d = [mtlDevice newBufferWithLength:8192 options:default_storage_mode]; + stats.mem_alloc(buffer_bindings_1d.allocatedSize + texture_bindings_2d.allocatedSize + + texture_bindings_3d.allocatedSize); /* command queue for non-tracing work on the GPU */ mtlGeneralCommandQueue = [mtlDevice newCommandQueue]; @@ -180,6 +185,8 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile arg_desc_tex.dataType = MTLDataTypePointer; arg_desc_tex.access = MTLArgumentAccessReadOnly; + arg_desc_tex.index = index++; + [ancillary_desc addObject:[arg_desc_tex copy]]; /* metal_buf_1d */ arg_desc_tex.index = index++; [ancillary_desc addObject:[arg_desc_tex copy]]; /* metal_tex_2d */ arg_desc_tex.index = index++; @@ -253,22 +260,26 @@ MetalDevice::~MetalDevice() * existing_devices_mutex). */ thread_scoped_lock lock(existing_devices_mutex); - for (auto &tex : texture_slot_map) { - if (tex) { - [tex release]; - tex = nil; + int num_resources = texture_info.size(); + for (int res = 0; res < num_resources; res++) { + if (is_texture(texture_info[res])) { + [texture_slot_map[res] release]; + texture_slot_map[res] = nil; } } + flush_delayed_free_list(); if (texture_bindings_2d) { - stats.mem_free(texture_bindings_2d.allocatedSize + texture_bindings_3d.allocatedSize); - + stats.mem_free(buffer_bindings_1d.allocatedSize + texture_bindings_2d.allocatedSize + + texture_bindings_3d.allocatedSize); + [buffer_bindings_1d release]; [texture_bindings_2d release]; [texture_bindings_3d release]; } [mtlTextureArgEncoder release]; [mtlBufferKernelParamsEncoder release]; + [mtlBufferArgEncoder release]; [mtlASArgEncoder release]; [mtlAncillaryArgEncoder release]; [mtlGeneralCommandQueue release]; @@ -332,6 +343,9 @@ string MetalDevice::preprocess_source(MetalPipelineType pso_type, const uint ker break; case METAL_GPU_APPLE: global_defines += "#define __KERNEL_METAL_APPLE__\n"; +# ifdef WITH_NANOVDB + global_defines += "#define WITH_NANOVDB\n"; +# endif break; } @@ -582,6 +596,11 @@ void MetalDevice::compile_and_load(int device_id, MetalPipelineType pso_type) } } +bool MetalDevice::is_texture(const TextureInfo &tex) +{ + return (tex.depth > 0 || tex.height > 0); +} + void MetalDevice::load_texture_info() { if (need_texture_info) { @@ -593,21 +612,20 @@ void MetalDevice::load_texture_info() for (int tex = 0; tex < num_textures; tex++) { uint64_t offset = tex * sizeof(void *); - - id metal_texture = texture_slot_map[tex]; - if (!metal_texture) { - [mtlTextureArgEncoder setArgumentBuffer:texture_bindings_2d offset:offset]; - [mtlTextureArgEncoder setTexture:nil atIndex:0]; - [mtlTextureArgEncoder setArgumentBuffer:texture_bindings_3d offset:offset]; - [mtlTextureArgEncoder setTexture:nil atIndex:0]; - } - else { + if (is_texture(texture_info[tex]) && texture_slot_map[tex]) { + id metal_texture = texture_slot_map[tex]; MTLTextureType type = metal_texture.textureType; [mtlTextureArgEncoder setArgumentBuffer:texture_bindings_2d offset:offset]; [mtlTextureArgEncoder setTexture:type == MTLTextureType2D ? metal_texture : nil atIndex:0]; [mtlTextureArgEncoder setArgumentBuffer:texture_bindings_3d offset:offset]; [mtlTextureArgEncoder setTexture:type == MTLTextureType3D ? metal_texture : nil atIndex:0]; } + else { + [mtlTextureArgEncoder setArgumentBuffer:texture_bindings_2d offset:offset]; + [mtlTextureArgEncoder setTexture:nil atIndex:0]; + [mtlTextureArgEncoder setArgumentBuffer:texture_bindings_3d offset:offset]; + [mtlTextureArgEncoder setTexture:nil atIndex:0]; + } } if (default_storage_mode == MTLResourceStorageModeManaged) { [texture_bindings_2d didModifyRange:NSMakeRange(0, num_textures * sizeof(void *))]; @@ -780,7 +798,6 @@ void MetalDevice::generic_free(device_memory &mem) mem.shared_pointer = 0; /* Free device memory. */ - delayed_free_list.push_back(mmem.mtlBuffer); mmem.mtlBuffer = nil; } @@ -1020,7 +1037,7 @@ void MetalDevice::global_free(device_memory &mem) void MetalDevice::tex_alloc_as_buffer(device_texture &mem) { - generic_alloc(mem); + MetalDevice::MetalMem *mmem = generic_alloc(mem); generic_copy_to(mem); /* Resize once */ @@ -1029,27 +1046,32 @@ void MetalDevice::tex_alloc_as_buffer(device_texture &mem) /* Allocate some slots in advance, to reduce amount * of re-allocations. */ texture_info.resize(round_up(slot + 1, 128)); + texture_slot_map.resize(round_up(slot + 1, 128)); } - mem.info.data = (uint64_t)mem.device_pointer; - - /* Set Mapping and tag that we need to (re-)upload to device */ texture_info[slot] = mem.info; + uint64_t offset = slot * sizeof(void *); + [mtlBufferArgEncoder setArgumentBuffer:buffer_bindings_1d offset:offset]; + [mtlBufferArgEncoder setBuffer:mmem->mtlBuffer offset:0 atIndex:0]; + texture_info[slot].data = *(uint64_t *)((uint64_t)buffer_bindings_1d.contents + offset); + texture_slot_map[slot] = nil; need_texture_info = true; } void MetalDevice::tex_alloc(device_texture &mem) { /* Check that dimensions fit within maximum allowable size. + * If 1D texture is allocated, use 1D buffer. * See: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ - if (mem.data_width > 16384 || mem.data_height > 16384) { - set_error(string_printf( - "Texture exceeds maximum allowed size of 16384 x 16384 (requested: %zu x %zu)", - mem.data_width, - mem.data_height)); - return; + if (mem.data_height > 0) { + if (mem.data_width > 16384 || mem.data_height > 16384) { + set_error(string_printf( + "Texture exceeds maximum allowed size of 16384 x 16384 (requested: %zu x %zu)", + mem.data_width, + mem.data_height)); + return; + } } - MTLStorageMode storage_mode = MTLStorageModeManaged; if (@available(macos 10.15, *)) { if ([mtlDevice hasUnifiedMemory] && @@ -1189,8 +1211,9 @@ void MetalDevice::tex_alloc(device_texture &mem) bytesPerRow:src_pitch]; } else { - assert(0); /* 1D texture, using linear memory. */ + tex_alloc_as_buffer(mem); + return; } mem.device_pointer = (device_ptr)mtlTexture; @@ -1214,17 +1237,22 @@ void MetalDevice::tex_alloc(device_texture &mem) ssize_t min_buffer_length = sizeof(void *) * texture_info.size(); if (!texture_bindings_2d || (texture_bindings_2d.length < min_buffer_length)) { if (texture_bindings_2d) { + delayed_free_list.push_back(buffer_bindings_1d); delayed_free_list.push_back(texture_bindings_2d); delayed_free_list.push_back(texture_bindings_3d); - stats.mem_free(texture_bindings_2d.allocatedSize + texture_bindings_3d.allocatedSize); + stats.mem_free(buffer_bindings_1d.allocatedSize + texture_bindings_2d.allocatedSize + + texture_bindings_3d.allocatedSize); } + buffer_bindings_1d = [mtlDevice newBufferWithLength:min_buffer_length + options:default_storage_mode]; texture_bindings_2d = [mtlDevice newBufferWithLength:min_buffer_length options:default_storage_mode]; texture_bindings_3d = [mtlDevice newBufferWithLength:min_buffer_length options:default_storage_mode]; - stats.mem_alloc(texture_bindings_2d.allocatedSize + texture_bindings_3d.allocatedSize); + stats.mem_alloc(buffer_bindings_1d.allocatedSize + texture_bindings_2d.allocatedSize + + texture_bindings_3d.allocatedSize); } } @@ -1251,12 +1279,18 @@ void MetalDevice::tex_alloc(device_texture &mem) void MetalDevice::tex_free(device_texture &mem) { + if (mem.data_depth == 0 && mem.data_height == 0) { + generic_free(mem); + return; + } + if (metal_mem_map.count(&mem)) { std::lock_guard lock(metal_mem_map_mutex); MetalMem &mmem = *metal_mem_map.at(&mem); assert(texture_slot_map[mem.slot] == mmem.mtlTexture); - texture_slot_map[mem.slot] = nil; + if (texture_slot_map[mem.slot] == mmem.mtlTexture) + texture_slot_map[mem.slot] = nil; if (mmem.mtlTexture) { /* Free bindless texture. */ diff --git a/intern/cycles/device/metal/queue.mm b/intern/cycles/device/metal/queue.mm index b824b75ccf4..a7c136669ed 100644 --- a/intern/cycles/device/metal/queue.mm +++ b/intern/cycles/device/metal/queue.mm @@ -477,17 +477,21 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, [metal_device_->mtlAncillaryArgEncoder setBuffer:metal_device_->texture_bindings_3d offset:0 atIndex:1]; + [metal_device_->mtlAncillaryArgEncoder setBuffer:metal_device_->buffer_bindings_1d + offset:0 + atIndex:2]; + if (@available(macos 12.0, *)) { if (metal_device_->use_metalrt) { if (metal_device_->bvhMetalRT) { id accel_struct = metal_device_->bvhMetalRT->accel_struct; - [metal_device_->mtlAncillaryArgEncoder setAccelerationStructure:accel_struct atIndex:2]; + [metal_device_->mtlAncillaryArgEncoder setAccelerationStructure:accel_struct atIndex:3]; [metal_device_->mtlAncillaryArgEncoder setBuffer:metal_device_->blas_buffer offset:0 - atIndex:7]; + atIndex:8]; [metal_device_->mtlAncillaryArgEncoder setBuffer:metal_device_->blas_lookup_buffer offset:0 - atIndex:8]; + atIndex:9]; } for (int table = 0; table < METALRT_TABLE_NUM; table++) { @@ -497,13 +501,13 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, atIndex:1]; [metal_device_->mtlAncillaryArgEncoder setIntersectionFunctionTable:metal_kernel_pso->intersection_func_table[table] - atIndex:3 + table]; + atIndex:4 + table]; [mtlComputeCommandEncoder useResource:metal_kernel_pso->intersection_func_table[table] usage:MTLResourceUsageRead]; } else { [metal_device_->mtlAncillaryArgEncoder setIntersectionFunctionTable:nil - atIndex:3 + table]; + atIndex:4 + table]; } } } @@ -874,6 +878,7 @@ void MetalDeviceQueue::prepare_resources(DeviceKernel kernel) /* ancillaries */ [mtlComputeEncoder_ useResource:metal_device_->texture_bindings_2d usage:MTLResourceUsageRead]; [mtlComputeEncoder_ useResource:metal_device_->texture_bindings_3d usage:MTLResourceUsageRead]; + [mtlComputeEncoder_ useResource:metal_device_->buffer_bindings_1d usage:MTLResourceUsageRead]; } id MetalDeviceQueue::get_compute_encoder(DeviceKernel kernel) diff --git a/intern/cycles/kernel/device/gpu/image.h b/intern/cycles/kernel/device/gpu/image.h index a8c72645569..60bf1890072 100644 --- a/intern/cycles/kernel/device/gpu/image.h +++ b/intern/cycles/kernel/device/gpu/image.h @@ -5,13 +5,14 @@ CCL_NAMESPACE_BEGIN -#ifdef WITH_NANOVDB -# define NDEBUG /* Disable "assert" in device code */ -# define NANOVDB_USE_INTRINSICS -# include "nanovdb/NanoVDB.h" -# include "nanovdb/util/SampleFromVoxels.h" +#if !defined __KERNEL_METAL__ +# ifdef WITH_NANOVDB +# define NDEBUG /* Disable "assert" in device code */ +# define NANOVDB_USE_INTRINSICS +# include "nanovdb/NanoVDB.h" +# include "nanovdb/util/SampleFromVoxels.h" +# endif #endif - /* w0, w1, w2, and w3 are the four cubic B-spline basis functions. */ ccl_device float cubic_w0(float a) { @@ -126,7 +127,7 @@ kernel_tex_image_interp_tricubic(ccl_global const TextureInfo &info, float x, fl #ifdef WITH_NANOVDB template ccl_device typename nanovdb::NanoGrid::ValueType kernel_tex_image_interp_tricubic_nanovdb( - S &s, float x, float y, float z) + ccl_private S &s, float x, float y, float z) { float px = floorf(x); float py = floorf(y); @@ -157,13 +158,19 @@ ccl_device typename nanovdb::NanoGrid::ValueType kernel_tex_image_interp_tric g1y * (g0x * s(Vec3f(x0, y1, z1)) + g1x * s(Vec3f(x1, y1, z1)))); } +# if defined(__KERNEL_METAL__) +template +__attribute__((noinline)) typename nanovdb::NanoGrid::ValueType kernel_tex_image_interp_nanovdb( + ccl_global const TextureInfo &info, float x, float y, float z, uint interpolation) +# else template ccl_device_noinline typename nanovdb::NanoGrid::ValueType kernel_tex_image_interp_nanovdb( ccl_global const TextureInfo &info, float x, float y, float z, uint interpolation) +# endif { using namespace nanovdb; - NanoGrid *const grid = (NanoGrid *)info.data; + ccl_global NanoGrid *const grid = (ccl_global NanoGrid *)info.data; typedef typename nanovdb::NanoGrid::AccessorType AccessorType; AccessorType acc = grid->getAccessor(); diff --git a/intern/cycles/kernel/device/metal/compat.h b/intern/cycles/kernel/device/metal/compat.h index 317bdc2eaae..93d0747cd69 100644 --- a/intern/cycles/kernel/device/metal/compat.h +++ b/intern/cycles/kernel/device/metal/compat.h @@ -290,6 +290,10 @@ typedef metal::raytracing::intersector metalrt_blas_intersector_t /* texture bindings and sampler setup */ +struct Buffer1DParamsMetal { + device float *buf; +}; + struct Texture2DParamsMetal { texture2d tex; }; @@ -306,6 +310,7 @@ struct MetalRTBlasWrapper { struct MetalAncillaries { device Texture2DParamsMetal *textures_2d; device Texture3DParamsMetal *textures_3d; + device Buffer1DParamsMetal *buffers; #ifdef __METALRT__ metalrt_as_type accel_struct; diff --git a/intern/cycles/kernel/device/metal/context_begin.h b/intern/cycles/kernel/device/metal/context_begin.h index 60d7e36589c..a03add1a4c1 100644 --- a/intern/cycles/kernel/device/metal/context_begin.h +++ b/intern/cycles/kernel/device/metal/context_begin.h @@ -3,6 +3,13 @@ // clang-format off +#ifdef WITH_NANOVDB +# define NDEBUG /* Disable "assert" in device code */ +# define NANOVDB_USE_INTRINSICS +# include "nanovdb/NanoVDB.h" +# include "nanovdb/util/SampleFromVoxels.h" +#endif + /* Open the Metal kernel context class * Necessary to access resource bindings */ class MetalKernelContext { diff --git a/intern/cycles/kernel/integrator/state_template.h b/intern/cycles/kernel/integrator/state_template.h index 0fc7ed655d3..e60f2fa4c31 100644 --- a/intern/cycles/kernel/integrator/state_template.h +++ b/intern/cycles/kernel/integrator/state_template.h @@ -122,7 +122,7 @@ KERNEL_STRUCT_MEMBER(guiding, bool, use_surface_guiding, KERNEL_FEATURE_PATH_GUI KERNEL_STRUCT_MEMBER(guiding, float, sample_surface_guiding_rand, KERNEL_FEATURE_PATH_GUIDING) /* The probability to use surface guiding (i.e., diffuse sampling prob * guiding prob)*/ KERNEL_STRUCT_MEMBER(guiding, float, surface_guiding_sampling_prob, KERNEL_FEATURE_PATH_GUIDING) -/* Probability of sampling a BSSRDF closure instead of a BSDF closure*/ +/* Probability of sampling a BSSRDF closure instead of a BSDF closure. */ KERNEL_STRUCT_MEMBER(guiding, float, bssrdf_sampling_prob, KERNEL_FEATURE_PATH_GUIDING) /* If volume guiding is enabled */ KERNEL_STRUCT_MEMBER(guiding, bool, use_volume_guiding, KERNEL_FEATURE_PATH_GUIDING) diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index 3c5698b4218..2b9e356e0de 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -1177,7 +1177,7 @@ void LightManager::device_update(Device *device, void LightManager::device_free(Device *, DeviceScene *dscene, const bool free_background) { - /* to-do: check if the light tree member variables need to be wrapped in a conditional too*/ + /* TODO: check if the light tree member variables need to be wrapped in a conditional too. */ dscene->light_tree_nodes.free(); dscene->light_tree_emitters.free(); dscene->light_to_tree.free(); diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index cf810caf778..899aaa7de22 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1195,24 +1195,89 @@ int GHOST_XrGetControllerModelData(GHOST_XrContextHandle xr_context, #ifdef WITH_VULKAN_BACKEND /** - * Return VULKAN handles for the given context. + * Get Vulkan handles for the given context. + * + * These handles are the same for a given context. + * Should should only be called when using a Vulkan context. + * Other contexts will not return any handles and leave the + * handles where the parameters are referring to unmodified. + * + * \param context: GHOST context handle of a vulkan context to + * get the Vulkan handles from. + * \param r_instance: After calling this function the VkInstance + * referenced by this parameter will contain the VKInstance handle + * of the context associated with the `context` parameter. + * \param r_physical_device: After calling this function the VkPhysicalDevice + * referenced by this parameter will contain the VKPhysicalDevice handle + * of the context associated with the `context` parameter. + * \param r_device: After calling this function the VkDevice + * referenced by this parameter will contain the VKDevice handle + * of the context associated with the `context` parameter. + * \param r_graphic_queue_family: After calling this function the uint32_t + * referenced by this parameter will contain the graphic queue family id + * of the context associated with the `context` parameter. + * \param r_queue: After calling this function the VkQueue + * referenced by this parameter will contain the VKQueue handle + * of the context associated with the `context` parameter. */ void GHOST_GetVulkanHandles(GHOST_ContextHandle context, void *r_instance, void *r_physical_device, void *r_device, - uint32_t *r_graphic_queue_family); + uint32_t *r_graphic_queue_family, + void *r_queue); /** - * Return VULKAN back-buffer resources handles for the given window. + * Return Vulkan command buffer. + * + * Command buffers are different for each image in the swap chain. + * At the start of each frame the correct command buffer should be + * retrieved with this function. + * + * Should should only be called when using a Vulkan context. + * Other contexts will not return any handles and leave the + * handles where the parameters are referring to unmodified. + * + * \param context: GHOST context handle to a vulkan context to get the + * command queue from. + * \param r_command_buffer: After calling this function the VkCommandBuffer + * referenced by this parameter will contain the VKCommandBuffer handle + * of the current back buffer (when swap chains are enabled) or + * it will contain a general VkCommandQueue. + */ +void GHOST_GetVulkanCommandBuffer(GHOST_ContextHandle context, void *r_command_buffer); + +/** + * Gets the Vulkan backbuffer related resource handles associated with the Vulkan context. + * Needs to be called after each swap event as the backbuffer will change. + * + * Should should only be called when using a Vulkan context with an active swap chain. + * Other contexts will not return any handles and leave the + * handles where the parameters are referring to unmodified. + * + * \param windowhandle: GHOST window handle to a window to get the resource from. + * \param r_image: After calling this function the VkImage + * referenced by this parameter will contain the VKImage handle + * of the current back buffer. + * \param r_framebuffer: After calling this function the VkFramebuffer + * referenced by this parameter will contain the VKFramebuffer handle + * of the current back buffer. + * \param r_render_pass: After calling this function the VkRenderPass + * referenced by this parameter will contain the VKRenderPass handle + * of the current back buffer. + * \param r_extent: After calling this function the VkExtent2D + * referenced by this parameter will contain the size of the + * frame buffer and image in pixels. + * \param r_fb_id: After calling this function the uint32_t + * referenced by this parameter will contain the id of the + * framebuffer of the current back buffer. */ void GHOST_GetVulkanBackbuffer(GHOST_WindowHandle windowhandle, - void *image, - void *framebuffer, - void *command_buffer, - void *render_pass, - void *extent, - uint32_t *fb_id); + void *r_image, + void *r_framebuffer, + void *r_render_pass, + void *r_extent, + uint32_t *r_fb_id); #endif diff --git a/intern/ghost/GHOST_IContext.h b/intern/ghost/GHOST_IContext.h index 52863e8c061..8499e6b2905 100644 --- a/intern/ghost/GHOST_IContext.h +++ b/intern/ghost/GHOST_IContext.h @@ -40,19 +40,84 @@ class GHOST_IContext { virtual unsigned int getDefaultFramebuffer() = 0; - virtual GHOST_TSuccess getVulkanHandles(void *, void *, void *, uint32_t *) = 0; + /** + * Get Vulkan handles for the given context. + * + * These handles are the same for a given context. + * Should should only be called when using a Vulkan context. + * Other contexts will not return any handles and leave the + * handles where the parameters are referring to unmodified. + * + * \param r_instance: After calling this function the VkInstance + * referenced by this parameter will contain the VKInstance handle + * of the context associated with the `context` parameter. + * \param r_physical_device: After calling this function the VkPhysicalDevice + * referenced by this parameter will contain the VKPhysicalDevice handle + * of the context associated with the `context` parameter. + * \param r_device: After calling this function the VkDevice + * referenced by this parameter will contain the VKDevice handle + * of the context associated with the `context` parameter. + * \param r_graphic_queue_family: After calling this function the uint32_t + * referenced by this parameter will contain the graphic queue family id + * of the context associated with the `context` parameter. + * \param r_queue: After calling this function the VkQueue + * referenced by this parameter will contain the VKQueue handle + * of the context associated with the `context` parameter. + * \returns GHOST_kFailure when context isn't a Vulkan context. + * GHOST_kSuccess when the context is a Vulkan context and the + * handles have been set. + */ + virtual GHOST_TSuccess getVulkanHandles(void *r_instance, + void *r_physical_device, + void *r_device, + uint32_t *r_graphic_queue_family, + void *r_queue) = 0; /** - * Gets the Vulkan framebuffer related resource handles associated with the Vulkan context. - * Needs to be called after each swap events as the framebuffer will change. - * \return A boolean success indicator. + * Return Vulkan command buffer. + * + * Command buffers are different for each image in the swap chain. + * At the start of each frame the correct command buffer should be + * retrieved with this function. + * + * \param r_command_buffer: After calling this function the VkCommandBuffer + * referenced by this parameter will contain the VKCommandBuffer handle + * of the current back buffer (when swap chains are enabled) or + * it will contain a general VkCommandQueue. + * \returns GHOST_kFailure when context isn't a Vulkan context. + * GHOST_kSuccess when the context is a Vulkan context and the + * handles have been set. */ - virtual GHOST_TSuccess getVulkanBackbuffer(void *image, - void *framebuffer, - void *command_buffer, - void *render_pass, - void *extent, - uint32_t *fb_id) = 0; + virtual GHOST_TSuccess getVulkanCommandBuffer(void *r_command_buffer) = 0; + + /** + * Gets the Vulkan backbuffer related resource handles associated with the Vulkan context. + * Needs to be called after each swap event as the backbuffer will change. + * + * \param r_image: After calling this function the VkImage + * referenced by this parameter will contain the VKImage handle + * of the current back buffer. + * \param r_framebuffer: After calling this function the VkFramebuffer + * referenced by this parameter will contain the VKFramebuffer handle + * of the current back buffer. + * \param r_render_pass: After calling this function the VkRenderPass + * referenced by this parameter will contain the VKRenderPass handle + * of the current back buffer. + * \param r_extent: After calling this function the VkExtent2D + * referenced by this parameter will contain the size of the + * frame buffer and image in pixels. + * \param r_fb_id: After calling this function the uint32_t + * referenced by this parameter will contain the id of the + * framebuffer of the current back buffer. + * \returns GHOST_kFailure when context isn't a Vulkan context. + * GHOST_kSuccess when the context is a Vulkan context and the + * handles have been set. + */ + virtual GHOST_TSuccess getVulkanBackbuffer(void *r_image, + void *r_framebuffer, + void *r_render_pass, + void *r_extent, + uint32_t *r_fb_id) = 0; virtual GHOST_TSuccess swapBuffers() = 0; diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h index 403f9e388cc..4fc24b6ca86 100644 --- a/intern/ghost/GHOST_IWindow.h +++ b/intern/ghost/GHOST_IWindow.h @@ -217,7 +217,6 @@ class GHOST_IWindow { */ virtual GHOST_TSuccess getVulkanBackbuffer(void *image, void *framebuffer, - void *command_buffer, void *render_pass, void *extent, uint32_t *fb_id) = 0; diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index a3c1eedc9c0..986f88205c7 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -1203,22 +1203,29 @@ void GHOST_GetVulkanHandles(GHOST_ContextHandle contexthandle, void *r_instance, void *r_physical_device, void *r_device, - uint32_t *r_graphic_queue_family) + uint32_t *r_graphic_queue_family, + void *r_queue) { GHOST_IContext *context = (GHOST_IContext *)contexthandle; - context->getVulkanHandles(r_instance, r_physical_device, r_device, r_graphic_queue_family); + context->getVulkanHandles( + r_instance, r_physical_device, r_device, r_graphic_queue_family, r_queue); +} + +void GHOST_GetVulkanCommandBuffer(GHOST_ContextHandle contexthandle, void *r_command_buffer) +{ + GHOST_IContext *context = (GHOST_IContext *)contexthandle; + context->getVulkanCommandBuffer(r_command_buffer); } void GHOST_GetVulkanBackbuffer(GHOST_WindowHandle windowhandle, void *image, void *framebuffer, - void *command_buffer, void *render_pass, void *extent, uint32_t *fb_id) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; - window->getVulkanBackbuffer(image, framebuffer, command_buffer, render_pass, extent, fb_id); + window->getVulkanBackbuffer(image, framebuffer, render_pass, extent, fb_id); } #endif /* WITH_VULKAN */ diff --git a/intern/ghost/intern/GHOST_Context.h b/intern/ghost/intern/GHOST_Context.h index d4cc0d18e55..6fd212f7eb3 100644 --- a/intern/ghost/intern/GHOST_Context.h +++ b/intern/ghost/intern/GHOST_Context.h @@ -136,27 +136,88 @@ class GHOST_Context : public GHOST_IContext { } /** - * Gets the Vulkan context related resource handles. - * \return A boolean success indicator. + * Get Vulkan handles for the given context. + * + * These handles are the same for a given context. + * Should should only be called when using a Vulkan context. + * Other contexts will not return any handles and leave the + * handles where the parameters are referring to unmodified. + * + * \param r_instance: After calling this function the VkInstance + * referenced by this parameter will contain the VKInstance handle + * of the context associated with the `context` parameter. + * \param r_physical_device: After calling this function the VkPhysicalDevice + * referenced by this parameter will contain the VKPhysicalDevice handle + * of the context associated with the `context` parameter. + * \param r_device: After calling this function the VkDevice + * referenced by this parameter will contain the VKDevice handle + * of the context associated with the `context` parameter. + * \param r_graphic_queue_family: After calling this function the uint32_t + * referenced by this parameter will contain the graphic queue family id + * of the context associated with the `context` parameter. + * \param r_queue: After calling this function the VkQueue + * referenced by this parameter will contain the VKQueue handle + * of the context associated with the `context` parameter. + * \returns GHOST_kFailure when context isn't a Vulkan context. + * GHOST_kSuccess when the context is a Vulkan context and the + * handles have been set. */ virtual GHOST_TSuccess getVulkanHandles(void * /*r_instance*/, void * /*r_physical_device*/, void * /*r_device*/, - uint32_t * /*r_graphic_queue_family*/) override + uint32_t * /*r_graphic_queue_family*/, + void * /*r_queue*/) override { return GHOST_kFailure; }; /** - * Gets the Vulkan framebuffer related resource handles associated with the Vulkan context. - * Needs to be called after each swap events as the framebuffer will change. - * \return A boolean success indicator. + * Return Vulkan command buffer. + * + * Command buffers are different for each image in the swap chain. + * At the start of each frame the correct command buffer should be + * retrieved with this function. + * + * \param r_command_buffer: After calling this function the VkCommandBuffer + * referenced by this parameter will contain the VKCommandBuffer handle + * of the current back buffer (when swap chains are enabled) or + * it will contain a general VkCommandQueue. + * \returns GHOST_kFailure when context isn't a Vulkan context. + * GHOST_kSuccess when the context is a Vulkan context and the + * handles have been set. */ - virtual GHOST_TSuccess getVulkanBackbuffer(void * /*image*/, - void * /*framebuffer*/, - void * /*command_buffer*/, - void * /*render_pass*/, - void * /*extent*/, + virtual GHOST_TSuccess getVulkanCommandBuffer(void * /*r_command_buffer*/) override + { + return GHOST_kFailure; + }; + + /** + * Gets the Vulkan backbuffer related resource handles associated with the Vulkan context. + * Needs to be called after each swap event as the backbuffer will change. + * + * \param r_image: After calling this function the VkImage + * referenced by this parameter will contain the VKImage handle + * of the current back buffer. + * \param r_framebuffer: After calling this function the VkFramebuffer + * referenced by this parameter will contain the VKFramebuffer handle + * of the current back buffer. + * \param r_render_pass: After calling this function the VkRenderPass + * referenced by this parameter will contain the VKRenderPass handle + * of the current back buffer. + * \param r_extent: After calling this function the VkExtent2D + * referenced by this parameter will contain the size of the + * frame buffer and image in pixels. + * \param r_fb_id: After calling this function the uint32_t + * referenced by this parameter will contain the id of the + * framebuffer of the current back buffer. + * \returns GHOST_kFailure when context isn't a Vulkan context. + * GHOST_kSuccess when the context is a Vulkan context and the + * handles have been set. + */ + virtual GHOST_TSuccess getVulkanBackbuffer(void * /*r_image*/, + void * /*r_framebuffer*/, + void * /*r_render_pass*/, + void * /*r_extent*/, uint32_t * /*fb_id*/) override { return GHOST_kFailure; diff --git a/intern/ghost/intern/GHOST_ContextVK.cpp b/intern/ghost/intern/GHOST_ContextVK.cpp index 4ee48243234..3f9b197939a 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cpp +++ b/intern/ghost/intern/GHOST_ContextVK.cpp @@ -288,19 +288,14 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers() return GHOST_kSuccess; } -GHOST_TSuccess GHOST_ContextVK::getVulkanBackbuffer(void *image, - void *framebuffer, - void *command_buffer, - void *render_pass, - void *extent, - uint32_t *fb_id) +GHOST_TSuccess GHOST_ContextVK::getVulkanBackbuffer( + void *image, void *framebuffer, void *render_pass, void *extent, uint32_t *fb_id) { if (m_swapchain == VK_NULL_HANDLE) { return GHOST_kFailure; } *((VkImage *)image) = m_swapchain_images[m_currentImage]; *((VkFramebuffer *)framebuffer) = m_swapchain_framebuffers[m_currentImage]; - *((VkCommandBuffer *)command_buffer) = m_command_buffers[m_currentImage]; *((VkRenderPass *)render_pass) = m_render_pass; *((VkExtent2D *)extent) = m_render_extent; *fb_id = m_swapchain_id * 10 + m_currentFrame; @@ -311,12 +306,30 @@ GHOST_TSuccess GHOST_ContextVK::getVulkanBackbuffer(void *image, GHOST_TSuccess GHOST_ContextVK::getVulkanHandles(void *r_instance, void *r_physical_device, void *r_device, - uint32_t *r_graphic_queue_family) + uint32_t *r_graphic_queue_family, + void *r_queue) { *((VkInstance *)r_instance) = m_instance; *((VkPhysicalDevice *)r_physical_device) = m_physical_device; *((VkDevice *)r_device) = m_device; *r_graphic_queue_family = m_queue_family_graphic; + *((VkQueue *)r_queue) = m_graphic_queue; + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::getVulkanCommandBuffer(void *r_command_buffer) +{ + if (m_command_buffers.empty()) { + return GHOST_kFailure; + } + + if (m_swapchain == VK_NULL_HANDLE) { + *((VkCommandBuffer *)r_command_buffer) = m_command_buffers[0]; + } + else { + *((VkCommandBuffer *)r_command_buffer) = m_command_buffers[m_currentImage]; + } return GHOST_kSuccess; } @@ -520,6 +533,9 @@ static GHOST_TSuccess getGraphicQueueFamily(VkPhysicalDevice device, uint32_t *r *r_queue_index = 0; for (const auto &queue_family : queue_families) { + /* Every vulkan implementation by spec must have one queue family that support both graphics + * and compute pipelines. We select this one; compute only queue family hints at async compute + * implementations.*/ if ((queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) && (queue_family.queueFlags & VK_QUEUE_COMPUTE_BIT)) { return GHOST_kSuccess; @@ -619,16 +635,36 @@ static GHOST_TSuccess selectPresentMode(VkPhysicalDevice device, return GHOST_kFailure; } -GHOST_TSuccess GHOST_ContextVK::createCommandBuffers() +GHOST_TSuccess GHOST_ContextVK::createCommandPools() { - m_command_buffers.resize(m_swapchain_image_views.size()); - VkCommandPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; poolInfo.queueFamilyIndex = m_queue_family_graphic; VK_CHECK(vkCreateCommandPool(m_device, &poolInfo, NULL, &m_command_pool)); + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffer() +{ + assert(m_command_pool != VK_NULL_HANDLE); + assert(m_command_buffers.size() == 0); + m_command_buffers.resize(1); + VkCommandBufferAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = m_command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = static_cast(m_command_buffers.size()); + + VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data())); + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_ContextVK::createGraphicsCommandBuffers() +{ + assert(m_command_pool != VK_NULL_HANDLE); + m_command_buffers.resize(m_swapchain_image_views.size()); VkCommandBufferAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; @@ -637,7 +673,6 @@ GHOST_TSuccess GHOST_ContextVK::createCommandBuffers() alloc_info.commandBufferCount = static_cast(m_command_buffers.size()); VK_CHECK(vkAllocateCommandBuffers(m_device, &alloc_info, m_command_buffers.data())); - return GHOST_kSuccess; } @@ -776,7 +811,7 @@ GHOST_TSuccess GHOST_ContextVK::createSwapchain() VK_CHECK(vkCreateFence(m_device, &fence_info, NULL, &m_in_flight_fences[i])); } - createCommandBuffers(); + createGraphicsCommandBuffers(); return GHOST_kSuccess; } @@ -841,6 +876,13 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() extensions_device.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); } + extensions_device.push_back("VK_KHR_dedicated_allocation"); + extensions_device.push_back("VK_KHR_get_memory_requirements2"); + /* Enable MoltenVK required instance extensions.*/ +#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME + requireExtension( + extensions_available, extensions_enabled, "VK_KHR_get_physical_device_properties2"); +#endif VkApplicationInfo app_info = {}; app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; @@ -903,6 +945,15 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() return GHOST_kFailure; } +#ifdef VK_MVK_MOLTENVK_EXTENSION_NAME + /* According to the Vulkan specs, when `VK_KHR_portability_subset` is available it should be + * enabled. See + * https://vulkan.lunarg.com/doc/view/1.2.198.1/mac/1.2-extensions/vkspec.html#VUID-VkDeviceCreateInfo-pProperties-04451*/ + if (device_extensions_support(m_physical_device, {VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME})) { + extensions_device.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); + } +#endif + vector queue_create_infos; { @@ -962,11 +1013,14 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext() vkGetDeviceQueue(m_device, m_queue_family_graphic, 0, &m_graphic_queue); + createCommandPools(); if (use_window_surface) { vkGetDeviceQueue(m_device, m_queue_family_present, 0, &m_present_queue); - createSwapchain(); } + else { + createGraphicsCommandBuffer(); + } return GHOST_kSuccess; } diff --git a/intern/ghost/intern/GHOST_ContextVK.h b/intern/ghost/intern/GHOST_ContextVK.h index 1a2d38bc701..0daa9e40027 100644 --- a/intern/ghost/intern/GHOST_ContextVK.h +++ b/intern/ghost/intern/GHOST_ContextVK.h @@ -113,18 +113,17 @@ class GHOST_ContextVK : public GHOST_Context { GHOST_TSuccess getVulkanHandles(void *r_instance, void *r_physical_device, void *r_device, - uint32_t *r_graphic_queue_family); + uint32_t *r_graphic_queue_family, + void *r_queue); + GHOST_TSuccess getVulkanCommandBuffer(void *r_command_buffer); + /** * Gets the Vulkan framebuffer related resource handles associated with the Vulkan context. * Needs to be called after each swap events as the framebuffer will change. * \return A boolean success indicator. */ - GHOST_TSuccess getVulkanBackbuffer(void *image, - void *framebuffer, - void *command_buffer, - void *render_pass, - void *extent, - uint32_t *fb_id); + GHOST_TSuccess getVulkanBackbuffer( + void *image, void *framebuffer, void *render_pass, void *extent, uint32_t *fb_id); /** * Sets the swap interval for swapBuffers. @@ -200,6 +199,8 @@ class GHOST_ContextVK : public GHOST_Context { GHOST_TSuccess pickPhysicalDevice(std::vector required_exts); GHOST_TSuccess createSwapchain(); GHOST_TSuccess destroySwapchain(); - GHOST_TSuccess createCommandBuffers(); + GHOST_TSuccess createCommandPools(); + GHOST_TSuccess createGraphicsCommandBuffers(); + GHOST_TSuccess createGraphicsCommandBuffer(); GHOST_TSuccess recordCommandBuffers(); }; diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 9fc69dabe00..2a9186ba1ae 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1826,7 +1826,10 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam, if (!window->m_mousePresent) { WINTAB_PRINTF("HWND %p mouse enter\n", window->getHWND()); TRACKMOUSEEVENT tme = {sizeof(tme)}; - tme.dwFlags = TME_LEAVE; + /* Request WM_MOUSELEAVE message when the cursor leaves the client area, and + * WM_MOUSEHOVER message after 50ms when in the client area. */ + tme.dwFlags = TME_LEAVE | TME_HOVER; + tme.dwHoverTime = 50; tme.hwndTrack = hwnd; TrackMouseEvent(&tme); window->m_mousePresent = true; @@ -1843,6 +1846,35 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam, break; } + case WM_MOUSEHOVER: { + /* Mouse Tracking is now off. TrackMouseEvent restarts in MouseMove. */ + window->m_mousePresent = false; + + /* Auto-focus only occurs within Blender windows, not with _other_ applications. */ + HWND old_hwnd = ::GetFocus(); + if (hwnd != old_hwnd) { + HWND new_parent = ::GetParent(hwnd); + HWND old_parent = ::GetParent(old_hwnd); + if (hwnd == old_parent || old_hwnd == new_parent) { + /* Child to its parent, parent to its child. */ + ::SetFocus(hwnd); + } + else if (new_parent != HWND_DESKTOP && new_parent == old_parent) { + /* Between siblings of same parent. */ + ::SetFocus(hwnd); + } + else if (!new_parent && !old_parent) { + /* Between main windows that don't overlap. */ + RECT new_rect, old_rect, dest_rect; + ::GetWindowRect(hwnd, &new_rect); + ::GetWindowRect(old_hwnd, &old_rect); + if (!IntersectRect(&dest_rect, &new_rect, &old_rect)) { + ::SetFocus(hwnd); + } + } + } + break; + } case WM_MOUSEWHEEL: { /* The WM_MOUSEWHEEL message is sent to the focus window * when the mouse wheel is rotated. The DefWindowProc diff --git a/intern/ghost/intern/GHOST_Window.cpp b/intern/ghost/intern/GHOST_Window.cpp index 202f803f710..a1444bd5c8a 100644 --- a/intern/ghost/intern/GHOST_Window.cpp +++ b/intern/ghost/intern/GHOST_Window.cpp @@ -109,13 +109,12 @@ uint GHOST_Window::getDefaultFramebuffer() GHOST_TSuccess GHOST_Window::getVulkanBackbuffer(void *image, void *framebuffer, - void *command_buffer, void *render_pass, void *extent, uint32_t *fb_id) { return m_context->getVulkanBackbuffer( - image, framebuffer, command_buffer, render_pass, extent, fb_id); + image, framebuffer, render_pass, extent, fb_id); } GHOST_TSuccess GHOST_Window::activateDrawingContext() diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h index 04ce9fed950..e008fcc72c1 100644 --- a/intern/ghost/intern/GHOST_Window.h +++ b/intern/ghost/intern/GHOST_Window.h @@ -274,12 +274,8 @@ class GHOST_Window : public GHOST_IWindow { * Needs to be called after each swap events as the framebuffer will change. * \return A boolean success indicator. */ - virtual GHOST_TSuccess getVulkanBackbuffer(void *image, - void *framebuffer, - void *command_buffer, - void *render_pass, - void *extent, - uint32_t *fb_id) override; + virtual GHOST_TSuccess getVulkanBackbuffer( + void *image, void *framebuffer, void *render_pass, void *extent, uint32_t *fb_id) override; /** * Returns the window user data. diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index 066ff4aa03a..843f6f62c73 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -306,14 +306,23 @@ static void gwl_window_frame_update_from_pending(GWL_Window *win); #ifdef USE_EVENT_BACKGROUND_THREAD enum eGWL_PendingWindowActions { - PENDING_FRAME_CONFIGURE = 0, - PENDING_EGL_RESIZE, + /** + * The state of the window frame has changed, apply the state from #GWL_Window::frame_pending. + */ + PENDING_WINDOW_FRAME_CONFIGURE = 0, + /** The EGL buffer must be resized to match #GWL_WindowFrame::size. */ + PENDING_EGL_WINDOW_RESIZE, # ifdef GHOST_OPENGL_ALPHA + /** Draw an opaque region behind the window. */ PENDING_OPAQUE_SET, # endif - PENDING_SCALE_UPDATE, + /** + * The DPI for a monitor has changed or the monitors (outputs) + * this window is visible on may have changed. Recalculate the windows scale. + */ + PENDING_OUTPUT_SCALE_UPDATE, }; -# define PENDING_NUM (PENDING_SCALE_UPDATE + 1) +# define PENDING_NUM (PENDING_OUTPUT_SCALE_UPDATE + 1) static void gwl_window_pending_actions_tag(GWL_Window *win, enum eGWL_PendingWindowActions type) { @@ -323,10 +332,10 @@ static void gwl_window_pending_actions_tag(GWL_Window *win, enum eGWL_PendingWin static void gwl_window_pending_actions_handle(GWL_Window *win) { - if (win->pending_actions[PENDING_FRAME_CONFIGURE].exchange(false)) { + if (win->pending_actions[PENDING_WINDOW_FRAME_CONFIGURE].exchange(false)) { gwl_window_frame_update_from_pending(win); } - if (win->pending_actions[PENDING_EGL_RESIZE].exchange(false)) { + if (win->pending_actions[PENDING_EGL_WINDOW_RESIZE].exchange(false)) { wl_egl_window_resize(win->egl_window, UNPACK2(win->frame.size), 0, 0); } # ifdef GHOST_OPENGL_ALPHA @@ -334,7 +343,7 @@ static void gwl_window_pending_actions_handle(GWL_Window *win) win->ghost_window->setOpaque(); } # endif - if (win->pending_actions[PENDING_SCALE_UPDATE].exchange(false)) { + if (win->pending_actions[PENDING_OUTPUT_SCALE_UPDATE].exchange(false)) { win->ghost_window->outputs_changed_update_scale(); } } @@ -342,9 +351,10 @@ static void gwl_window_pending_actions_handle(GWL_Window *win) #endif /* USE_EVENT_BACKGROUND_THREAD */ /** - * Update the window's #GWL_WindowFrame + * Update the window's #GWL_WindowFrame. + * The caller must handle locking & run from the main thread. */ -static void gwl_window_frame_update_from_pending_lockfree(GWL_Window *win) +static void gwl_window_frame_update_from_pending_no_lock(GWL_Window *win) { #ifdef USE_EVENT_BACKGROUND_THREAD GHOST_ASSERT(win->ghost_system->main_thread_id == std::this_thread::get_id(), @@ -381,7 +391,7 @@ static void gwl_window_frame_update_from_pending(GWL_Window *win) #ifdef USE_EVENT_BACKGROUND_THREAD std::lock_guard lock_frame_guard{win->frame_pending_mutex}; #endif - gwl_window_frame_update_from_pending_lockfree(win); + gwl_window_frame_update_from_pending_no_lock(win); } /** \} */ @@ -576,12 +586,12 @@ static void frame_handle_configure(struct libdecor_frame *frame, GHOST_SystemWayland *system = win->ghost_system; const bool is_main_thread = system->main_thread_id == std::this_thread::get_id(); if (!is_main_thread) { - gwl_window_pending_actions_tag(win, PENDING_FRAME_CONFIGURE); + gwl_window_pending_actions_tag(win, PENDING_WINDOW_FRAME_CONFIGURE); } else # endif { - gwl_window_frame_update_from_pending_lockfree(win); + gwl_window_frame_update_from_pending_no_lock(win); } } } @@ -671,7 +681,7 @@ static void xdg_surface_handle_configure(void *data, if (!is_main_thread) { /* NOTE(@ideasman42): this only gets one redraw, * I could not find a case where this causes problems. */ - gwl_window_pending_actions_tag(win, PENDING_FRAME_CONFIGURE); + gwl_window_pending_actions_tag(win, PENDING_WINDOW_FRAME_CONFIGURE); } else #endif @@ -1373,7 +1383,7 @@ bool GHOST_WindowWayland::outputs_changed_update_scale() { #ifdef USE_EVENT_BACKGROUND_THREAD if (system_->main_thread_id != std::this_thread::get_id()) { - gwl_window_pending_actions_tag(window_, PENDING_SCALE_UPDATE); + gwl_window_pending_actions_tag(window_, PENDING_OUTPUT_SCALE_UPDATE); return false; } #endif diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 9dc7b184b12..332de7e266c 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -7726,22 +7726,6 @@ inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccccc" /> - - - - + + + + + + + + + + + + + - - - - - - - - - pos[0], font->pos[1]); - if (!BLI_rcti_inside_rcti(&font->clip_rec, &rect_test)) { return; } diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 7c01a9205fc..76558b9ba28 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -41,6 +41,7 @@ typedef enum eAttrDomainMask { ATTR_DOMAIN_MASK_CURVE = (1 << 4), ATTR_DOMAIN_MASK_ALL = (1 << 5) - 1 } eAttrDomainMask; +ENUM_OPERATORS(eAttrDomainMask, ATTR_DOMAIN_MASK_ALL); #define ATTR_DOMAIN_AS_MASK(domain) ((eAttrDomainMask)((1 << (int)(domain)))) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 26f45b79aef..72b89dd634c 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -17,17 +17,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 305 +#define BLENDER_VERSION 306 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta - -/* TODO proper version bump. */ +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 10 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index 67b282ff2ad..ba13d9d6a6c 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -108,7 +108,7 @@ BVHTree *bvhtree_from_editmesh_verts( */ BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data, struct BMEditMesh *em, - const blender::BitVector<> &mask, + blender::BitSpan mask, int verts_num_active, float epsilon, int tree_type, @@ -124,7 +124,7 @@ BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data, BVHTree *bvhtree_from_mesh_verts_ex(struct BVHTreeFromMesh *data, const float (*vert_positions)[3], int verts_num, - const blender::BitVector<> &verts_mask, + blender::BitSpan verts_mask, int verts_num_active, float epsilon, int tree_type, @@ -138,7 +138,7 @@ BVHTree *bvhtree_from_editmesh_edges( */ BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data, struct BMEditMesh *em, - const blender::BitVector<> &edges_mask, + blender::BitSpan edges_mask, int edges_num_active, float epsilon, int tree_type, @@ -156,7 +156,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(struct BVHTreeFromMesh *data, const float (*vert_positions)[3], const struct MEdge *edge, int edges_num, - const blender::BitVector<> &edges_mask, + blender::BitSpan edges_mask, int edges_num_active, float epsilon, int tree_type, @@ -170,7 +170,7 @@ BVHTree *bvhtree_from_editmesh_looptri( */ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data, struct BMEditMesh *em, - const blender::BitVector<> &mask, + blender::BitSpan mask, int looptri_num_active, float epsilon, int tree_type, @@ -184,7 +184,7 @@ BVHTree *bvhtree_from_mesh_looptri_ex(struct BVHTreeFromMesh *data, const struct MLoop *mloop, const struct MLoopTri *looptri, int looptri_num, - const blender::BitVector<> &mask, + blender::BitSpan mask, int looptri_num_active, float epsilon, int tree_type, diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 9cddc39f43c..297cd3c2433 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -282,6 +282,7 @@ bool CustomData_has_layer(const struct CustomData *data, int type); * Returns the number of layers with this type. */ int CustomData_number_of_layers(const struct CustomData *data, int type); +int CustomData_number_of_anonymous_layers(const struct CustomData *data, int type); int CustomData_number_of_layers_typemask(const struct CustomData *data, eCustomDataMask mask); /** diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 62b80a627bb..69cf1f1edf6 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -373,6 +373,8 @@ bool BKE_fcurve_calc_range( /** * Calculate the extents of F-Curve's data. + * \param range Only calculate the bounds of the FCurve in the given range. + * Does the full range if NULL. */ bool BKE_fcurve_calc_bounds(const struct FCurve *fcu, float *xmin, @@ -380,7 +382,8 @@ bool BKE_fcurve_calc_bounds(const struct FCurve *fcu, float *ymin, float *ymax, bool do_sel_only, - bool include_handles); + bool include_handles, + const float range[2]); /** * Return an array of keyed frames, rounded to `interval`. diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index be447ab0ecc..e2383c66e8f 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -767,47 +767,6 @@ void BKE_mesh_polys_flip(const struct MPoly *mpoly, struct CustomData *ldata, int totpoly); -/* Merge verts. */ -/* Enum for merge_mode of #BKE_mesh_merge_verts. - * Refer to mesh_merge.c for details. */ -enum { - MESH_MERGE_VERTS_DUMP_IF_MAPPED, - MESH_MERGE_VERTS_DUMP_IF_EQUAL, -}; -/** - * Merge Verts - * - * This frees the given mesh and returns a new mesh. - * - * \param vtargetmap: The table that maps vertices to target vertices. a value of -1 - * indicates a vertex is a target, and is to be kept. - * This array is aligned with 'mesh->totvert' - * \warning \a vtargetmap must **not** contain any chained mapping (v1 -> v2 -> v3 etc.), - * this is not supported and will likely generate corrupted geometry. - * - * \param tot_vtargetmap: The number of non '-1' values in vtargetmap. (not the size) - * - * \param merge_mode: enum with two modes. - * - #MESH_MERGE_VERTS_DUMP_IF_MAPPED - * When called by the Mirror Modifier, - * In this mode it skips any faces that have all vertices merged (to avoid creating pairs - * of faces sharing the same set of vertices) - * - #MESH_MERGE_VERTS_DUMP_IF_EQUAL - * When called by the Array Modifier, - * In this mode, faces where all vertices are merged are double-checked, - * to see whether all target vertices actually make up a poly already. - * Indeed it could be that all of a poly's vertices are merged, - * but merged to vertices that do not make up a single poly, - * in which case the original poly should not be dumped. - * Actually this later behavior could apply to the Mirror Modifier as well, - * but the additional checks are costly and not necessary in the case of mirror, - * because each vertex is only merged to its own mirror. - */ -struct Mesh *BKE_mesh_merge_verts(struct Mesh *mesh, - const int *vtargetmap, - int tot_vtargetmap, - int merge_mode); - /** * Account for custom-data such as UVs becoming detached because of imprecision * in custom-data interpolation. diff --git a/source/blender/blenkernel/BKE_mesh_mirror.h b/source/blender/blenkernel/BKE_mesh_mirror.h index 582f107e4d9..0ad6b382d0f 100644 --- a/source/blender/blenkernel/BKE_mesh_mirror.h +++ b/source/blender/blenkernel/BKE_mesh_mirror.h @@ -35,7 +35,9 @@ struct Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(struct MirrorModi struct Object *ob, const struct Mesh *mesh, int axis, - bool use_correct_order_on_merge); + bool use_correct_order_on_merge, + int **r_vert_merge_map, + int *r_vert_merge_map_len); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h index 0fa3d16d328..a58b4166ba5 100644 --- a/source/blender/blenkernel/BKE_nla.h +++ b/source/blender/blenkernel/BKE_nla.h @@ -41,10 +41,10 @@ struct PropertyRNA; */ void BKE_nlastrip_free(struct NlaStrip *strip, bool do_id_user); /** - * Remove the given NLA track from the set of NLA tracks, free the track's data, - * and the track itself. + * Remove & Frees all NLA strips from the given NLA track, + * then frees (doesn't remove) the track itself. */ -void BKE_nlatrack_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user); +void BKE_nlatrack_free(struct NlaTrack *nlt, bool do_id_user); /** * Free the elements of type NLA Tracks provided in the given list, but do not free * the list itself since that is not free-standing @@ -95,6 +95,17 @@ struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt, struct NlaTrack *prev, bool is_liboverride); +/** + * Removes the given NLA track from the list of tracks provided. + */ +void BKE_nlatrack_remove(ListBase *tracks, struct NlaTrack *nlt); + +/** + * Remove the given NLA track from the list of NLA tracks, free the track's data, + * and the track itself. + */ +void BKE_nlatrack_remove_and_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user); + /** * Create a NLA Strip referencing the given Action. */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a0f6405a6ac..d7d17600683 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -98,6 +98,7 @@ class NodeMultiFunctionBuilder; class GeoNodeExecParams; class NodeDeclaration; class NodeDeclarationBuilder; +class GatherAddNodeSearchParams; class GatherLinkSearchOpParams; } // namespace nodes namespace realtime_compositor { @@ -122,6 +123,10 @@ using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket using NodeGatherSocketLinkOperationsFunction = void (*)(blender::nodes::GatherLinkSearchOpParams ¶ms); +/* Adds node add menu operations that are specific to this node type. */ +using NodeGatherAddOperationsFunction = + void (*)(blender::nodes::GatherAddNodeSearchParams ¶ms); + using NodeGetCompositorOperationFunction = blender::realtime_compositor::NodeOperation *(*)(blender::realtime_compositor::Context &context, blender::nodes::DNode node); using NodeGetCompositorShaderNodeFunction = @@ -135,6 +140,7 @@ typedef void *NodeGeometryExecFunction; typedef void *NodeDeclareFunction; typedef void *NodeDeclareDynamicFunction; typedef void *NodeGatherSocketLinkOperationsFunction; +typedef void *NodeGatherAddOperationsFunction; typedef void *SocketGetCPPTypeFunction; typedef void *SocketGetGeometryNodesCPPTypeFunction; typedef void *SocketGetGeometryNodesCPPValueFunction; @@ -353,6 +359,13 @@ typedef struct bNodeType { */ NodeGatherSocketLinkOperationsFunction gather_link_search_ops; + /** + * Add to the list of search items gathered by the add-node search. The default behavior of + * adding a single item with the node name is usually enough, but node types can have any number + * of custom search items. + */ + NodeGatherAddOperationsFunction gather_add_node_search_ops; + /** True when the node cannot be muted. */ bool no_muting; diff --git a/source/blender/blenkernel/BKE_subdiv_foreach.h b/source/blender/blenkernel/BKE_subdiv_foreach.h deleted file mode 100644 index 9afa5040772..00000000000 --- a/source/blender/blenkernel/BKE_subdiv_foreach.h +++ /dev/null @@ -1,165 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2018 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup bke - */ - -#pragma once - -#include "BLI_sys_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct Mesh; -struct Subdiv; -struct SubdivForeachContext; -struct SubdivToMeshSettings; - -typedef bool (*SubdivForeachTopologyInformationCb)(const struct SubdivForeachContext *context, - int num_vertices, - int num_edges, - int num_loops, - int num_polygons, - const int *subdiv_polygon_offset); - -typedef void (*SubdivForeachVertexFromCornerCb)(const struct SubdivForeachContext *context, - void *tls, - int ptex_face_index, - float u, - float v, - int coarse_vertex_index, - int coarse_poly_index, - int coarse_corner, - int subdiv_vertex_index); - -typedef void (*SubdivForeachVertexFromEdgeCb)(const struct SubdivForeachContext *context, - void *tls, - int ptex_face_index, - float u, - float v, - int coarse_edge_index, - int coarse_poly_index, - int coarse_corner, - int subdiv_vertex_index); - -typedef void (*SubdivForeachVertexInnerCb)(const struct SubdivForeachContext *context, - void *tls, - int ptex_face_index, - float u, - float v, - int coarse_poly_index, - int coarse_corner, - int subdiv_vertex_index); - -typedef void (*SubdivForeachEdgeCb)(const struct SubdivForeachContext *context, - void *tls, - int coarse_edge_index, - int subdiv_edge_index, - bool is_loose, - int subdiv_v1, - int subdiv_v2); - -typedef void (*SubdivForeachLoopCb)(const struct SubdivForeachContext *context, - void *tls, - int ptex_face_index, - float u, - float v, - int coarse_loop_index, - int coarse_poly_index, - int coarse_corner, - int subdiv_loop_index, - int subdiv_vertex_index, - int subdiv_edge_index); - -typedef void (*SubdivForeachPolygonCb)(const struct SubdivForeachContext *context, - void *tls, - int coarse_poly_index, - int subdiv_poly_index, - int start_loop_index, - int num_loops); - -typedef void (*SubdivForeachLooseCb)(const struct SubdivForeachContext *context, - void *tls, - int coarse_vertex_index, - int subdiv_vertex_index); - -typedef void (*SubdivForeachVertexOfLooseEdgeCb)(const struct SubdivForeachContext *context, - void *tls, - int coarse_edge_index, - float u, - int subdiv_vertex_index); - -typedef struct SubdivForeachContext { - /* Is called when topology information becomes available. - * Is only called once. - * - * NOTE: If this callback returns false, the foreach loop is aborted. - */ - SubdivForeachTopologyInformationCb topology_info; - /* These callbacks are called from every ptex which shares "emitting" - * vertex or edge. - */ - SubdivForeachVertexFromCornerCb vertex_every_corner; - SubdivForeachVertexFromEdgeCb vertex_every_edge; - /* Those callbacks are run once per subdivision vertex, ptex is undefined - * as in it will be whatever first ptex face happened to be traversed in - * the multi-threaded environment and which shares "emitting" vertex or - * edge. - */ - SubdivForeachVertexFromCornerCb vertex_corner; - SubdivForeachVertexFromEdgeCb vertex_edge; - /* Called exactly once, always corresponds to a single ptex face. */ - SubdivForeachVertexInnerCb vertex_inner; - /* Called once for each loose vertex. One loose coarse vertex corresponds - * to a single subdivision vertex. - */ - SubdivForeachLooseCb vertex_loose; - /* Called once per vertex created for loose edge. */ - SubdivForeachVertexOfLooseEdgeCb vertex_of_loose_edge; - /* NOTE: If subdivided edge does not come from coarse edge, ORIGINDEX_NONE - * will be passed as coarse_edge_index. - */ - SubdivForeachEdgeCb edge; - /* NOTE: If subdivided loop does not come from coarse loop, ORIGINDEX_NONE - * will be passed as coarse_loop_index. - */ - SubdivForeachLoopCb loop; - SubdivForeachPolygonCb poly; - - /* User-defined pointer, to allow callbacks know something about context the - * traversal is happening for. - */ - void *user_data; - - /* Initial value of TLS data. */ - void *user_data_tls; - /* Size of TLS data. */ - size_t user_data_tls_size; - /* Function to free TLS storage. */ - void (*user_data_tls_free)(void *tls); -} SubdivForeachContext; - -/* Invokes callbacks in the order and with values which corresponds to creation - * of final subdivided mesh. - * - * Main goal is to abstract all the traversal routines to give geometry element - * indices (for vertices, edges, loops, polygons) in the same way as subdivision - * modifier will do for a dense mesh. - * - * Returns true if the whole topology was traversed, without any early exits. - * - * TODO(sergey): Need to either get rid of subdiv or of coarse_mesh. - * The main point here is to be able to get base level topology, which can be - * done with either of those. Having both of them is kind of redundant. - */ -bool BKE_subdiv_foreach_subdiv_geometry(struct Subdiv *subdiv, - const struct SubdivForeachContext *context, - const struct SubdivToMeshSettings *mesh_settings, - const struct Mesh *coarse_mesh); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/blenkernel/BKE_subdiv_foreach.hh b/source/blender/blenkernel/BKE_subdiv_foreach.hh new file mode 100644 index 00000000000..a573a62942d --- /dev/null +++ b/source/blender/blenkernel/BKE_subdiv_foreach.hh @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2018 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup bke + */ + +#pragma once + +#include "BLI_sys_types.h" + +struct Mesh; +struct Subdiv; +struct SubdivForeachContext; +struct SubdivToMeshSettings; + +using SubdivForeachTopologyInformationCb = bool (*)(const SubdivForeachContext *context, + int num_vertices, + int num_edges, + int num_loops, + int num_polygons, + const int *subdiv_polygon_offset); + +using SubdivForeachVertexFromCornerCb = void (*)(const SubdivForeachContext *context, + void *tls, + int ptex_face_index, + float u, + float v, + int coarse_vertex_index, + int coarse_poly_index, + int coarse_corner, + int subdiv_vertex_index); + +using SubdivForeachVertexFromEdgeCb = void (*)(const SubdivForeachContext *context, + void *tls, + int ptex_face_index, + float u, + float v, + int coarse_edge_index, + int coarse_poly_index, + int coarse_corner, + int subdiv_vertex_index); + +using SubdivForeachVertexInnerCb = void (*)(const SubdivForeachContext *context, + void *tls, + int ptex_face_index, + float u, + float v, + int coarse_poly_index, + int coarse_corner, + int subdiv_vertex_index); + +using SubdivForeachEdgeCb = void (*)(const SubdivForeachContext *context, + void *tls, + int coarse_edge_index, + int subdiv_edge_index, + bool is_loose, + int subdiv_v1, + int subdiv_v2); + +using SubdivForeachLoopCb = void (*)(const SubdivForeachContext *context, + void *tls, + int ptex_face_index, + float u, + float v, + int coarse_loop_index, + int coarse_poly_index, + int coarse_corner, + int subdiv_loop_index, + int subdiv_vertex_index, + int subdiv_edge_index); + +using SubdivForeachPolygonCb = void (*)(const SubdivForeachContext *context, + void *tls, + int coarse_poly_index, + int subdiv_poly_index, + int start_loop_index, + int num_loops); + +using SubdivForeachLooseCb = void (*)(const SubdivForeachContext *context, + void *tls, + int coarse_vertex_index, + int subdiv_vertex_index); + +using SubdivForeachVertexOfLooseEdgeCb = void (*)(const SubdivForeachContext *context, + void *tls, + int coarse_edge_index, + float u, + int subdiv_vertex_index); + +struct SubdivForeachContext { + /* Is called when topology information becomes available. + * Is only called once. + * + * NOTE: If this callback returns false, the foreach loop is aborted. + */ + SubdivForeachTopologyInformationCb topology_info; + /* These callbacks are called from every ptex which shares "emitting" + * vertex or edge. + */ + SubdivForeachVertexFromCornerCb vertex_every_corner; + SubdivForeachVertexFromEdgeCb vertex_every_edge; + /* Those callbacks are run once per subdivision vertex, ptex is undefined + * as in it will be whatever first ptex face happened to be traversed in + * the multi-threaded environment and which shares "emitting" vertex or + * edge. + */ + SubdivForeachVertexFromCornerCb vertex_corner; + SubdivForeachVertexFromEdgeCb vertex_edge; + /* Called exactly once, always corresponds to a single ptex face. */ + SubdivForeachVertexInnerCb vertex_inner; + /* Called once for each loose vertex. One loose coarse vertex corresponds + * to a single subdivision vertex. + */ + SubdivForeachLooseCb vertex_loose; + /* Called once per vertex created for loose edge. */ + SubdivForeachVertexOfLooseEdgeCb vertex_of_loose_edge; + /* NOTE: If subdivided edge does not come from coarse edge, ORIGINDEX_NONE + * will be passed as coarse_edge_index. + */ + SubdivForeachEdgeCb edge; + /* NOTE: If subdivided loop does not come from coarse loop, ORIGINDEX_NONE + * will be passed as coarse_loop_index. + */ + SubdivForeachLoopCb loop; + SubdivForeachPolygonCb poly; + + /* User-defined pointer, to allow callbacks know something about context the + * traversal is happening for. + */ + void *user_data; + + /* Initial value of TLS data. */ + void *user_data_tls; + /* Size of TLS data. */ + size_t user_data_tls_size; + /* Function to free TLS storage. */ + void (*user_data_tls_free)(void *tls); +}; + +/* Invokes callbacks in the order and with values which corresponds to creation + * of final subdivided mesh. + * + * Main goal is to abstract all the traversal routines to give geometry element + * indices (for vertices, edges, loops, polygons) in the same way as subdivision + * modifier will do for a dense mesh. + * + * Returns true if the whole topology was traversed, without any early exits. + * + * TODO(sergey): Need to either get rid of subdiv or of coarse_mesh. + * The main point here is to be able to get base level topology, which can be + * done with either of those. Having both of them is kind of redundant. + */ +bool BKE_subdiv_foreach_subdiv_geometry(Subdiv *subdiv, + const SubdivForeachContext *context, + const SubdivToMeshSettings *mesh_settings, + const Mesh *coarse_mesh); diff --git a/source/blender/blenkernel/BKE_subdiv_mesh.h b/source/blender/blenkernel/BKE_subdiv_mesh.hh similarity index 73% rename from source/blender/blenkernel/BKE_subdiv_mesh.h rename to source/blender/blenkernel/BKE_subdiv_mesh.hh index d1ccc60061c..5e57270a5bc 100644 --- a/source/blender/blenkernel/BKE_subdiv_mesh.h +++ b/source/blender/blenkernel/BKE_subdiv_mesh.hh @@ -9,16 +9,12 @@ #include "BLI_sys_types.h" -#ifdef __cplusplus -extern "C" { -#endif - struct Mesh; struct MeshElemMap; struct MEdge; struct Subdiv; -typedef struct SubdivToMeshSettings { +struct SubdivToMeshSettings { /* Resolution at which regular ptex (created for quad polygon) are being * evaluated. This defines how many vertices final mesh will have: every * regular ptex has resolution^2 vertices. Special (irregular, or ptex @@ -28,23 +24,20 @@ typedef struct SubdivToMeshSettings { int resolution; /* When true, only edges emitted from coarse ones will be displayed. */ bool use_optimal_display; -} SubdivToMeshSettings; +}; /* Create real hi-res mesh from subdivision, all geometry is "real". */ -struct Mesh *BKE_subdiv_to_mesh(struct Subdiv *subdiv, - const SubdivToMeshSettings *settings, - const struct Mesh *coarse_mesh); +Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv, + const SubdivToMeshSettings *settings, + const Mesh *coarse_mesh); /* Interpolate a position along the `coarse_edge` at the relative `u` coordinate. If `is_simple` is * false, this will perform a B-Spline interpolation using the edge neighbors, otherwise a linear * interpolation will be done base on the edge vertices. */ void BKE_subdiv_mesh_interpolate_position_on_edge(const float (*coarse_positions)[3], - const struct MEdge *coarse_edges, - const struct MeshElemMap *vert_to_edge_map, + const MEdge *coarse_edges, + const MeshElemMap *vert_to_edge_map, int coarse_edge_index, bool is_simple, float u, float pos_r[3]); -#ifdef __cplusplus -} -#endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 2291827e374..bad71c19afa 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -201,7 +201,6 @@ set(SRC intern/mesh_iterators.cc intern/mesh_legacy_convert.cc intern/mesh_mapping.cc - intern/mesh_merge.c intern/mesh_merge_customdata.cc intern/mesh_mirror.cc intern/mesh_normals.cc @@ -470,8 +469,8 @@ set(SRC BKE_subdiv_ccg.h BKE_subdiv_deform.h BKE_subdiv_eval.h - BKE_subdiv_foreach.h - BKE_subdiv_mesh.h + BKE_subdiv_foreach.hh + BKE_subdiv_mesh.hh BKE_subdiv_modifier.h BKE_subdiv_topology.h BKE_subsurf.h diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 8a6e1486701..2ba03b6d1cf 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -316,9 +316,6 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) cons GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner) const { - if (writable_ != Writable) { - return {}; - } CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return {}; diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index b429756e754..a1a74e3b81c 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -37,10 +37,6 @@ class BuiltinAttributeProvider { Creatable, NonCreatable, }; - enum WritableEnum { - Writable, - Readonly, - }; enum DeletableEnum { Deletable, NonDeletable, @@ -51,7 +47,6 @@ class BuiltinAttributeProvider { const eAttrDomain domain_; const eCustomDataType data_type_; const CreatableEnum createable_; - const WritableEnum writable_; const DeletableEnum deletable_; const AttributeValidator validator_; @@ -60,14 +55,12 @@ class BuiltinAttributeProvider { const eAttrDomain domain, const eCustomDataType data_type, const CreatableEnum createable, - const WritableEnum writable, const DeletableEnum deletable, AttributeValidator validator = {}) : name_(std::move(name)), domain_(domain), data_type_(data_type), createable_(createable), - writable_(writable), deletable_(deletable), validator_(validator) { @@ -205,20 +198,14 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { const eCustomDataType attribute_type, const eCustomDataType stored_type, const CreatableEnum creatable, - const WritableEnum writable, const DeletableEnum deletable, const CustomDataAccessInfo custom_data_access, const AsReadAttribute as_read_attribute, const AsWriteAttribute as_write_attribute, const UpdateOnChange update_on_write, const AttributeValidator validator = {}) - : BuiltinAttributeProvider(std::move(attribute_name), - domain, - attribute_type, - creatable, - writable, - deletable, - validator), + : BuiltinAttributeProvider( + std::move(attribute_name), domain, attribute_type, creatable, deletable, validator), stored_type_(stored_type), custom_data_access_(custom_data_access), as_read_attribute_(as_read_attribute), diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 9a66b6b7d96..cd2be70530b 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -263,6 +263,8 @@ static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_addres if (brush->gradient) { BLO_write_struct(writer, ColorBand, brush->gradient); } + + BKE_previewimg_blend_write(writer, brush->preview); } static void brush_blend_read_data(BlendDataReader *reader, ID *id) @@ -348,7 +350,9 @@ static void brush_blend_read_data(BlendDataReader *reader, ID *id) } } - brush->preview = nullptr; + BLO_read_data_address(reader, &brush->preview); + BKE_previewimg_blend_read(reader, brush->preview); + brush->icon_imbuf = nullptr; } diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index fc1072d4cb8..f29407b641b 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -29,6 +29,7 @@ #include "MEM_guardedalloc.h" +using blender::BitSpan; using blender::BitVector; using blender::float3; using blender::IndexRange; @@ -672,7 +673,7 @@ static BVHTree *bvhtree_from_editmesh_verts_create_tree(float epsilon, int tree_type, int axis, BMEditMesh *em, - const BitVector<> &verts_mask, + const BitSpan verts_mask, int verts_num_active) { BM_mesh_elem_table_ensure(em->bm, BM_VERT); @@ -706,7 +707,7 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon, int axis, const float (*positions)[3], const int verts_num, - const BitVector<> &verts_mask, + const BitSpan verts_mask, int verts_num_active) { if (!verts_mask.is_empty()) { @@ -737,7 +738,7 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon, BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data, BMEditMesh *em, - const BitVector<> &verts_mask, + const BitSpan verts_mask, int verts_num_active, float epsilon, int tree_type, @@ -764,7 +765,7 @@ BVHTree *bvhtree_from_editmesh_verts( BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data, const float (*vert_positions)[3], const int verts_num, - const BitVector<> &verts_mask, + const BitSpan verts_mask, int verts_num_active, float epsilon, int tree_type, @@ -794,7 +795,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon, int tree_type, int axis, BMEditMesh *em, - const BitVector<> &edges_mask, + const BitSpan edges_mask, int edges_num_active) { BM_mesh_elem_table_ensure(em->bm, BM_EDGE); @@ -833,7 +834,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon, static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3], const MEdge *edge, const int edge_num, - const BitVector<> &edges_mask, + const BitSpan edges_mask, int edges_num_active, float epsilon, int tree_type, @@ -871,7 +872,7 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3], BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data, BMEditMesh *em, - const BitVector<> &edges_mask, + const BitSpan edges_mask, int edges_num_active, float epsilon, int tree_type, @@ -899,7 +900,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data, const float (*vert_positions)[3], const MEdge *edge, const int edges_num, - const BitVector<> &edges_mask, + const BitSpan edges_mask, int edges_num_active, float epsilon, int tree_type, @@ -931,7 +932,7 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree(float epsilon, const float (*positions)[3], const MFace *face, const int faces_num, - const BitVector<> &faces_mask, + const BitSpan faces_mask, int faces_num_active) { if (faces_num == 0) { @@ -984,7 +985,7 @@ static BVHTree *bvhtree_from_editmesh_looptri_create_tree(float epsilon, int tree_type, int axis, BMEditMesh *em, - const BitVector<> &looptri_mask, + const BitSpan looptri_mask, int looptri_num_active) { const int looptri_num = em->tottri; @@ -1038,7 +1039,7 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree(float epsilon, const MLoop *mloop, const MLoopTri *looptri, const int looptri_num, - const BitVector<> &looptri_mask, + const BitSpan looptri_mask, int looptri_num_active) { if (!looptri_mask.is_empty()) { @@ -1079,7 +1080,7 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree(float epsilon, BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data, BMEditMesh *em, - const BitVector<> &looptri_mask, + const BitSpan looptri_mask, int looptri_num_active, float epsilon, int tree_type, @@ -1109,7 +1110,7 @@ BVHTree *bvhtree_from_mesh_looptri_ex(BVHTreeFromMesh *data, const struct MLoop *mloop, const struct MLoopTri *looptri, const int looptri_num, - const BitVector<> &looptri_mask, + const BitSpan looptri_mask, int looptri_num_active, float epsilon, int tree_type, diff --git a/source/blender/blenkernel/intern/cloth.cc b/source/blender/blenkernel/intern/cloth.cc index e300c4d5084..8924c4f3cd8 100644 --- a/source/blender/blenkernel/intern/cloth.cc +++ b/source/blender/blenkernel/intern/cloth.cc @@ -835,7 +835,7 @@ static bool cloth_from_object( static void cloth_from_mesh(ClothModifierData *clmd, const Object *ob, Mesh *mesh) { - const MLoop *mloop = BKE_mesh_loops(mesh); + const blender::Span loops = mesh->loops(); const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh); const uint mvert_num = mesh->totvert; const uint looptri_num = BKE_mesh_runtime_looptri_len(mesh); @@ -868,9 +868,10 @@ static void cloth_from_mesh(ClothModifierData *clmd, const Object *ob, Mesh *mes printf("cloth_free_modifier clmd->clothObject->looptri\n"); return; } - BKE_mesh_runtime_verttri_from_looptri(clmd->clothObject->tri, mloop, looptri, looptri_num); + BKE_mesh_runtime_verttri_from_looptri( + clmd->clothObject->tri, loops.data(), looptri, looptri_num); - clmd->clothObject->edges = BKE_mesh_edges(mesh); + clmd->clothObject->edges = mesh->edges().data(); /* Free the springs since they can't be correct if the vertices * changed. @@ -1288,8 +1289,8 @@ void cloth_parallel_transport_hair_frame(float mat[3][3], /* Add a shear and a bend spring between two verts within a poly. */ static bool cloth_add_shear_bend_spring(ClothModifierData *clmd, LinkNodePair *edgelist, - const MLoop *mloop, - const MPoly *mpoly, + const blender::Span loops, + const blender::Span polys, int i, int j, int k) @@ -1308,7 +1309,7 @@ static bool cloth_add_shear_bend_spring(ClothModifierData *clmd, } spring_verts_ordered_set( - spring, mloop[mpoly[i].loopstart + j].v, mloop[mpoly[i].loopstart + k].v); + spring, loops[polys[i].loopstart + j].v, loops[polys[i].loopstart + k].v); shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * @@ -1328,7 +1329,7 @@ static bool cloth_add_shear_bend_spring(ClothModifierData *clmd, spring->type |= CLOTH_SPRING_TYPE_BENDING; spring->la = k - j + 1; - spring->lb = mpoly[i].totloop - k + j + 1; + spring->lb = polys[i].totloop - k + j + 1; spring->pa = static_cast(MEM_mallocN(sizeof(*spring->pa) * spring->la, "spring poly")); if (!spring->pa) { @@ -1340,7 +1341,7 @@ static bool cloth_add_shear_bend_spring(ClothModifierData *clmd, return false; } - tmp_loop = mloop + mpoly[i].loopstart; + tmp_loop = &loops[polys[i].loopstart]; for (x = 0; x < spring->la; x++) { spring->pa[x] = tmp_loop[j + x].v; @@ -1350,7 +1351,7 @@ static bool cloth_add_shear_bend_spring(ClothModifierData *clmd, spring->pb[x] = tmp_loop[x].v; } - for (y = k; y < mpoly[i].totloop; x++, y++) { + for (y = k; y < polys[i].totloop; x++, y++) { spring->pb[x] = tmp_loop[y].v; } @@ -1474,9 +1475,9 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) uint numedges = uint(mesh->totedge); uint numpolys = uint(mesh->totpoly); float shrink_factor; - const MEdge *medge = BKE_mesh_edges(mesh); - const MPoly *mpoly = BKE_mesh_polys(mesh); - const MLoop *mloop = BKE_mesh_loops(mesh); + const blender::Span edges = mesh->edges(); + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); int index2 = 0; /* our second vertex index */ LinkNodePair *edgelist = nullptr; EdgeSet *edgeset = nullptr; @@ -1607,7 +1608,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) spring = (ClothSpring *)MEM_callocN(sizeof(ClothSpring), "cloth spring"); if (spring) { - spring_verts_ordered_set(spring, medge[i].v1, medge[i].v2); + spring_verts_ordered_set(spring, edges[i].v1, edges[i].v2); if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW && loose_edges.count > 0 && loose_edges.is_loose_bits[i]) { /* handle sewing (loose edges will be pulled together) */ @@ -1615,7 +1616,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) spring->lin_stiffness = 1.0f; spring->type = CLOTH_SPRING_TYPE_SEWING; - BLI_edgeset_insert(cloth->sew_edge_graph, medge[i].v1, medge[i].v2); + BLI_edgeset_insert(cloth->sew_edge_graph, edges[i].v1, edges[i].v2); } else { shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); @@ -1668,10 +1669,10 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) for (int i = 0; i < numpolys; i++) { /* Shear springs. */ /* Triangle faces already have shear springs due to structural geometry. */ - if (mpoly[i].totloop > 3) { - for (int j = 1; j < mpoly[i].totloop - 1; j++) { + if (polys[i].totloop > 3) { + for (int j = 1; j < polys[i].totloop - 1; j++) { if (j > 1) { - if (cloth_add_shear_bend_spring(clmd, edgelist, mloop, mpoly, i, 0, j)) { + if (cloth_add_shear_bend_spring(clmd, edgelist, loops, polys, i, 0, j)) { shear_springs++; if (clmd->sim_parms->bending_model == CLOTH_BENDING_ANGULAR) { @@ -1684,8 +1685,8 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) } } - for (int k = j + 2; k < mpoly[i].totloop; k++) { - if (cloth_add_shear_bend_spring(clmd, edgelist, mloop, mpoly, i, j, k)) { + for (int k = j + 2; k < polys[i].totloop; k++) { + if (cloth_add_shear_bend_spring(clmd, edgelist, loops, polys, i, j, k)) { shear_springs++; if (clmd->sim_parms->bending_model == CLOTH_BENDING_ANGULAR) { @@ -1702,9 +1703,9 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) /* Angular bending springs along struct springs. */ if (clmd->sim_parms->bending_model == CLOTH_BENDING_ANGULAR) { - const MLoop *ml = mloop + mpoly[i].loopstart; + const MLoop *ml = &loops[polys[i].loopstart]; - for (int j = 0; j < mpoly[i].totloop; j++, ml++) { + for (int j = 0; j < polys[i].totloop; j++, ml++) { BendSpringRef *curr_ref = &spring_ref[ml->e]; curr_ref->polys++; @@ -1718,13 +1719,13 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) spring->type |= CLOTH_SPRING_TYPE_BENDING; - spring->la = mpoly[curr_ref->index].totloop; - spring->lb = mpoly[i].totloop; + spring->la = polys[curr_ref->index].totloop; + spring->lb = polys[i].totloop; if (!cloth_bend_set_poly_vert_array( - &spring->pa, spring->la, &mloop[mpoly[curr_ref->index].loopstart]) || + &spring->pa, spring->la, &loops[polys[curr_ref->index].loopstart]) || !cloth_bend_set_poly_vert_array( - &spring->pb, spring->lb, &mloop[mpoly[i].loopstart])) { + &spring->pb, spring->lb, &loops[polys[i].loopstart])) { cloth_free_errorsprings(cloth, edgelist, spring_ref); return false; } @@ -1890,13 +1891,13 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) /* insert other near springs in edgeset AFTER bending springs are calculated (for selfcolls) */ for (int i = 0; i < numedges; i++) { /* struct springs */ - BLI_edgeset_add(edgeset, medge[i].v1, medge[i].v2); + BLI_edgeset_add(edgeset, edges[i].v1, edges[i].v2); } for (int i = 0; i < numpolys; i++) { /* edge springs */ - if (mpoly[i].totloop == 4) { - BLI_edgeset_add(edgeset, mloop[mpoly[i].loopstart + 0].v, mloop[mpoly[i].loopstart + 2].v); - BLI_edgeset_add(edgeset, mloop[mpoly[i].loopstart + 1].v, mloop[mpoly[i].loopstart + 3].v); + if (polys[i].totloop == 4) { + BLI_edgeset_add(edgeset, loops[polys[i].loopstart + 0].v, loops[polys[i].loopstart + 2].v); + BLI_edgeset_add(edgeset, loops[polys[i].loopstart + 1].v, loops[polys[i].loopstart + 3].v); } } diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 704bbb34f49..ee438cdf5bf 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -469,8 +469,12 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, const VArray resolution = curves.resolution(); const VArray cyclic = curves.cyclic(); - const VArraySpan handle_types_left{curves.handle_types_left()}; - const VArraySpan handle_types_right{curves.handle_types_right()}; + VArraySpan handle_types_left; + VArraySpan handle_types_right; + if (curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + handle_types_left = curves.handle_types_left(); + handle_types_right = curves.handle_types_right(); + } const VArray nurbs_orders = curves.nurbs_orders(); const VArray nurbs_knots_modes = curves.nurbs_knots_modes(); diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 7b9b0348dfb..2da8cb8aeca 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2989,6 +2989,19 @@ int CustomData_number_of_layers(const CustomData *data, const int type) return number; } +int CustomData_number_of_anonymous_layers(const CustomData *data, const int type) +{ + int number = 0; + + for (int i = 0; i < data->totlayer; i++) { + if (data->layers[i].type == type && data->layers[i].anonymous_id != nullptr) { + number++; + } + } + + return number; +} + int CustomData_number_of_layers_typemask(const CustomData *data, const eCustomDataMask mask) { int number = 0; diff --git a/source/blender/blenkernel/intern/data_transfer.cc b/source/blender/blenkernel/intern/data_transfer.cc index 225011f1556..abb081e34d7 100644 --- a/source/blender/blenkernel/intern/data_transfer.cc +++ b/source/blender/blenkernel/intern/data_transfer.cc @@ -265,20 +265,24 @@ static void data_transfer_mesh_attributes_transfer_active_color_string( const char *active_color_src = BKE_id_attributes_active_color_name(&mesh_src->id); if ((data_type == CD_PROP_COLOR) && - !BKE_id_attribute_search(&mesh_src->id, active_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { + !BKE_id_attribute_search( + &mesh_src->id, active_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { return; } else if ((data_type == CD_PROP_BYTE_COLOR) && - !BKE_id_attribute_search(&mesh_src->id, active_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) { + !BKE_id_attribute_search( + &mesh_src->id, active_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) { return; } if ((data_type == CD_PROP_COLOR) && - BKE_id_attribute_search(&mesh_dst->id, active_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { + BKE_id_attribute_search( + &mesh_dst->id, active_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { mesh_dst->active_color_attribute = BLI_strdup(active_color_src); } else if ((data_type == CD_PROP_BYTE_COLOR) && - BKE_id_attribute_search(&mesh_dst->id, active_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) { + BKE_id_attribute_search( + &mesh_dst->id, active_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) { mesh_dst->active_color_attribute = BLI_strdup(active_color_src); } else { @@ -305,20 +309,26 @@ static void data_transfer_mesh_attributes_transfer_default_color_string( const char *default_color_src = BKE_id_attributes_default_color_name(&mesh_src->id); if ((data_type == CD_PROP_COLOR) && - !BKE_id_attribute_search(&mesh_src->id, default_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { + !BKE_id_attribute_search( + &mesh_src->id, default_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { return; } - else if ((data_type == CD_PROP_BYTE_COLOR) && - !BKE_id_attribute_search(&mesh_src->id, default_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) { + else if ((data_type == CD_PROP_BYTE_COLOR) && !BKE_id_attribute_search(&mesh_src->id, + default_color_src, + CD_MASK_PROP_BYTE_COLOR, + ATTR_DOMAIN_MASK_COLOR)) { return; } if ((data_type == CD_PROP_COLOR) && - BKE_id_attribute_search(&mesh_dst->id, default_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { + BKE_id_attribute_search( + &mesh_dst->id, default_color_src, CD_MASK_PROP_COLOR, ATTR_DOMAIN_MASK_COLOR)) { mesh_dst->default_color_attribute = BLI_strdup(default_color_src); } - else if ((data_type == CD_PROP_BYTE_COLOR) && - BKE_id_attribute_search(&mesh_dst->id, default_color_src, CD_MASK_PROP_BYTE_COLOR, ATTR_DOMAIN_MASK_COLOR)) { + else if ((data_type == CD_PROP_BYTE_COLOR) && BKE_id_attribute_search(&mesh_dst->id, + default_color_src, + CD_MASK_PROP_BYTE_COLOR, + ATTR_DOMAIN_MASK_COLOR)) { mesh_dst->default_color_attribute = BLI_strdup(default_color_src); } else { @@ -344,12 +354,9 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src, const float(*positions_dst)[3] = BKE_mesh_vert_positions(me_dst); const int num_verts_dst = me_dst->totvert; - const MEdge *edges_dst = BKE_mesh_edges(me_dst); - const int num_edges_dst = me_dst->totedge; - const MPoly *polys_dst = BKE_mesh_polys(me_dst); - const int num_polys_dst = me_dst->totpoly; - const MLoop *loops_dst = BKE_mesh_loops(me_dst); - const int num_loops_dst = me_dst->totloop; + const blender::Span edges_dst = me_dst->edges(); + const blender::Span polys_dst = me_dst->polys(); + const blender::Span loops_dst = me_dst->loops(); CustomData *ldata_dst = &me_dst->ldata; const bool use_split_nors_dst = (me_dst->flag & ME_AUTOSMOOTH) != 0; @@ -369,7 +376,7 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src, const bool do_loop_nors_dst = (loop_nors_dst == nullptr); if (do_loop_nors_dst) { loop_nors_dst = static_cast( - CustomData_add_layer(ldata_dst, CD_NORMAL, CD_SET_DEFAULT, nullptr, num_loops_dst)); + CustomData_add_layer(ldata_dst, CD_NORMAL, CD_SET_DEFAULT, nullptr, loops_dst.size())); CustomData_set_layer_flag(ldata_dst, CD_NORMAL, CD_FLAG_TEMPORARY); } if (dirty_nors_dst || do_loop_nors_dst) { @@ -378,14 +385,14 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src, BKE_mesh_normals_loop_split(positions_dst, BKE_mesh_vertex_normals_ensure(me_dst), num_verts_dst, - edges_dst, - num_edges_dst, - loops_dst, + edges_dst.data(), + edges_dst.size(), + loops_dst.data(), loop_nors_dst, - num_loops_dst, - polys_dst, + loops_dst.size(), + polys_dst.data(), BKE_mesh_poly_normals_ensure(me_dst), - num_polys_dst, + polys_dst.size(), use_split_nors_dst, split_angle_dst, sharp_edges, @@ -412,10 +419,8 @@ static void data_transfer_dtdata_type_postprocess(Object * /*ob_src*/, /* Bake edited destination loop normals into custom normals again. */ const float(*positions_dst)[3] = BKE_mesh_vert_positions(me_dst); const int num_verts_dst = me_dst->totvert; - MEdge *edges_dst = BKE_mesh_edges_for_write(me_dst); - const int num_edges_dst = me_dst->totedge; - MPoly *polys_dst = BKE_mesh_polys_for_write(me_dst); - const int num_polys_dst = me_dst->totpoly; + const blender::Span edges_dst = me_dst->edges(); + blender::MutableSpan polys_dst = me_dst->polys_for_write(); MLoop *loops_dst = BKE_mesh_loops_for_write(me_dst); const int num_loops_dst = me_dst->totloop; CustomData *ldata_dst = &me_dst->ldata; @@ -439,14 +444,14 @@ static void data_transfer_dtdata_type_postprocess(Object * /*ob_src*/, BKE_mesh_normals_loop_custom_set(positions_dst, BKE_mesh_vertex_normals_ensure(me_dst), num_verts_dst, - edges_dst, - num_edges_dst, + edges_dst.data(), + edges_dst.size(), loops_dst, loop_nors_dst, num_loops_dst, - polys_dst, + polys_dst.data(), poly_nors_dst, - num_polys_dst, + polys_dst.size(), sharp_edges.span.data(), custom_nors_dst); sharp_edges.finish(); @@ -1119,8 +1124,8 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map, mix_mode, mix_factor, mix_weights, - BKE_mesh_polys(me_src), - BKE_mesh_polys_for_write(me_dst), + me_src->polys().data(), + me_dst->polys_for_write().data(), me_src->totpoly, me_dst->totpoly, elem_size, @@ -1497,13 +1502,12 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (DT_DATATYPE_IS_EDGE(dtdata_type)) { const float(*positions_dst)[3] = BKE_mesh_vert_positions_for_write(me_dst); const int num_verts_dst = me_dst->totvert; - const MEdge *edges_dst = BKE_mesh_edges(me_dst); - const int num_edges_dst = me_dst->totedge; + const blender::Span edges_dst = me_dst->edges(); if (!geom_map_init[EDATA]) { const int num_edges_src = me_src->totedge; - if ((map_edge_mode == MREMAP_MODE_TOPOLOGY) && (num_edges_dst != num_edges_src)) { + if ((map_edge_mode == MREMAP_MODE_TOPOLOGY) && (edges_dst.size() != num_edges_src)) { BKE_report(reports, RPT_ERROR, "Source and destination meshes do not have the same amount of edges, " @@ -1517,7 +1521,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, "None of the 'Face' mappings can be used in this case"); continue; } - if (ELEM(0, num_edges_dst, num_edges_src)) { + if (ELEM(0, edges_dst.size(), num_edges_src)) { BKE_report( reports, RPT_ERROR, @@ -1531,8 +1535,8 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, ray_radius, positions_dst, num_verts_dst, - edges_dst, - num_edges_dst, + edges_dst.data(), + edges_dst.size(), dirty_nors_dst, me_src, me_dst, @@ -1542,9 +1546,14 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (mdef && vg_idx != -1 && !weights[EDATA]) { weights[EDATA] = static_cast( - MEM_mallocN(sizeof(*weights[EDATA]) * size_t(num_edges_dst), __func__)); - BKE_defvert_extract_vgroup_to_edgeweights( - mdef, vg_idx, num_verts_dst, edges_dst, num_edges_dst, invert_vgroup, weights[EDATA]); + MEM_mallocN(sizeof(*weights[EDATA]) * size_t(edges_dst.size()), __func__)); + BKE_defvert_extract_vgroup_to_edgeweights(mdef, + vg_idx, + num_verts_dst, + edges_dst.data(), + edges_dst.size(), + invert_vgroup, + weights[EDATA]); } if (data_transfer_layersmapping_generate(&lay_map, @@ -1557,7 +1566,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, mix_mode, mix_factor, weights[EDATA], - num_edges_dst, + edges_dst.size(), use_create, use_delete, fromlayers, @@ -1578,12 +1587,9 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (DT_DATATYPE_IS_LOOP(dtdata_type)) { const float(*positions_dst)[3] = BKE_mesh_vert_positions(me_dst); const int num_verts_dst = me_dst->totvert; - const MEdge *edges_dst = BKE_mesh_edges(me_dst); - const int num_edges_dst = me_dst->totedge; - const MPoly *polys_dst = BKE_mesh_polys(me_dst); - const int num_polys_dst = me_dst->totpoly; - const MLoop *loops_dst = BKE_mesh_loops(me_dst); - const int num_loops_dst = me_dst->totloop; + const blender::Span edges_dst = me_dst->edges(); + const blender::Span polys_dst = me_dst->polys(); + const blender::Span loops_dst = me_dst->loops(); CustomData *ldata_dst = &me_dst->ldata; MeshRemapIslandsCalc island_callback = data_transfer_get_loop_islands_generator(cddata_type); @@ -1591,7 +1597,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (!geom_map_init[LDATA]) { const int num_loops_src = me_src->totloop; - if ((map_loop_mode == MREMAP_MODE_TOPOLOGY) && (num_loops_dst != num_loops_src)) { + if ((map_loop_mode == MREMAP_MODE_TOPOLOGY) && (loops_dst.size() != num_loops_src)) { BKE_report(reports, RPT_ERROR, "Source and destination meshes do not have the same amount of face corners, " @@ -1605,7 +1611,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, "None of the 'Edge' mappings can be used in this case"); continue; } - if (ELEM(0, num_loops_dst, num_loops_src)) { + if (ELEM(0, loops_dst.size(), num_loops_src)) { BKE_report( reports, RPT_ERROR, @@ -1620,12 +1626,12 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, me_dst, positions_dst, num_verts_dst, - edges_dst, - num_edges_dst, - loops_dst, - num_loops_dst, - polys_dst, - num_polys_dst, + edges_dst.data(), + edges_dst.size(), + loops_dst.data(), + loops_dst.size(), + polys_dst.data(), + polys_dst.size(), ldata_dst, (me_dst->flag & ME_AUTOSMOOTH) != 0, me_dst->smoothresh, @@ -1639,9 +1645,14 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (mdef && vg_idx != -1 && !weights[LDATA]) { weights[LDATA] = static_cast( - MEM_mallocN(sizeof(*weights[LDATA]) * size_t(num_loops_dst), __func__)); - BKE_defvert_extract_vgroup_to_loopweights( - mdef, vg_idx, num_verts_dst, loops_dst, num_loops_dst, invert_vgroup, weights[LDATA]); + MEM_mallocN(sizeof(*weights[LDATA]) * size_t(loops_dst.size()), __func__)); + BKE_defvert_extract_vgroup_to_loopweights(mdef, + vg_idx, + num_verts_dst, + loops_dst.data(), + loops_dst.size(), + invert_vgroup, + weights[LDATA]); } if (data_transfer_layersmapping_generate(&lay_map, @@ -1654,7 +1665,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, mix_mode, mix_factor, weights[LDATA], - num_loops_dst, + loops_dst.size(), use_create, use_delete, fromlayers, @@ -1675,15 +1686,13 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (DT_DATATYPE_IS_POLY(dtdata_type)) { const float(*positions_dst)[3] = BKE_mesh_vert_positions(me_dst); const int num_verts_dst = me_dst->totvert; - const MPoly *polys_dst = BKE_mesh_polys(me_dst); - const int num_polys_dst = me_dst->totpoly; - const MLoop *loops_dst = BKE_mesh_loops(me_dst); - const int num_loops_dst = me_dst->totloop; + const blender::Span polys_dst = me_dst->polys(); + const blender::Span loops_dst = me_dst->loops(); if (!geom_map_init[PDATA]) { const int num_polys_src = me_src->totpoly; - if ((map_poly_mode == MREMAP_MODE_TOPOLOGY) && (num_polys_dst != num_polys_src)) { + if ((map_poly_mode == MREMAP_MODE_TOPOLOGY) && (polys_dst.size() != num_polys_src)) { BKE_report(reports, RPT_ERROR, "Source and destination meshes do not have the same amount of faces, " @@ -1697,7 +1706,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, "None of the 'Edge' mappings can be used in this case"); continue; } - if (ELEM(0, num_polys_dst, num_polys_src)) { + if (ELEM(0, polys_dst.size(), num_polys_src)) { BKE_report( reports, RPT_ERROR, @@ -1711,9 +1720,9 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, ray_radius, me_dst, positions_dst, - loops_dst, - polys_dst, - num_polys_dst, + loops_dst.data(), + polys_dst.data(), + polys_dst.size(), me_src, &geom_map[PDATA]); geom_map_init[PDATA] = true; @@ -1721,14 +1730,14 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (mdef && vg_idx != -1 && !weights[PDATA]) { weights[PDATA] = static_cast( - MEM_mallocN(sizeof(*weights[PDATA]) * size_t(num_polys_dst), __func__)); + MEM_mallocN(sizeof(*weights[PDATA]) * size_t(polys_dst.size()), __func__)); BKE_defvert_extract_vgroup_to_polyweights(mdef, vg_idx, num_verts_dst, - loops_dst, - num_loops_dst, - polys_dst, - num_polys_dst, + loops_dst.data(), + loops_dst.size(), + polys_dst.data(), + polys_dst.size(), invert_vgroup, weights[PDATA]); } @@ -1743,7 +1752,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, mix_mode, mix_factor, weights[PDATA], - num_polys_dst, + polys_dst.size(), use_create, use_delete, fromlayers, diff --git a/source/blender/blenkernel/intern/dynamicpaint.cc b/source/blender/blenkernel/intern/dynamicpaint.cc index bfd391c4cc9..4ef9f018dc2 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.cc +++ b/source/blender/blenkernel/intern/dynamicpaint.cc @@ -1414,9 +1414,9 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const b /* For vertex format, count every vertex that is connected by an edge */ int numOfEdges = mesh->totedge; int numOfPolys = mesh->totpoly; - const MEdge *edges = BKE_mesh_edges(mesh); - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); + const blender::Span edges = mesh->edges(); + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); /* count number of edges per vertex */ for (int i = 0; i < numOfEdges; i++) { @@ -1479,7 +1479,7 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const b struct DynamicPaintSetInitColorData { const DynamicPaintSurface *surface; - const MLoop *mloop; + blender::Span loops; const float (*mloopuv)[2]; const MLoopTri *mlooptri; const MLoopCol *mloopcol; @@ -1497,7 +1497,7 @@ static void dynamic_paint_set_init_color_tex_to_vcol_cb(void *__restrict userdat const PaintSurfaceData *sData = data->surface->data; PaintPoint *pPoint = (PaintPoint *)sData->type_data; - const MLoop *mloop = data->mloop; + const blender::Span loops = data->loops; const MLoopTri *mlooptri = data->mlooptri; const float(*mloopuv)[2] = data->mloopuv; ImagePool *pool = data->pool; @@ -1509,7 +1509,7 @@ static void dynamic_paint_set_init_color_tex_to_vcol_cb(void *__restrict userdat for (int j = 3; j--;) { TexResult texres = {0}; - const uint vert = mloop[mlooptri[i].tri[j]].v; + const uint vert = loops[mlooptri[i].tri[j]].v; /* remap to [-1.0, 1.0] */ uv[0] = mloopuv[mlooptri[i].tri[j]][0] * 2.0f - 1.0f; @@ -1618,7 +1618,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface else if (surface->init_color_type == MOD_DPAINT_INITIAL_TEXTURE) { Tex *tex = surface->init_texture; - const MLoop *mloop = BKE_mesh_loops(mesh); + const blender::Span loops = mesh->loops(); const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(mesh); const int tottri = BKE_mesh_runtime_looptri_len(mesh); @@ -1644,7 +1644,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface DynamicPaintSetInitColorData data{}; data.surface = surface; - data.mloop = mloop; + data.loops = loops; data.mlooptri = mlooptri; data.mloopuv = mloopuv; data.pool = pool; @@ -1676,16 +1676,15 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface /* For vertex surface, just copy colors from #MLoopCol. */ if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { - const MLoop *mloop = BKE_mesh_loops(mesh); - const int totloop = mesh->totloop; + const blender::Span loops = mesh->loops(); const MLoopCol *col = static_cast( CustomData_get_layer_named(&mesh->ldata, CD_PROP_BYTE_COLOR, surface->init_layername)); if (!col) { return; } - for (int i = 0; i < totloop; i++) { - rgba_uchar_to_float(pPoint[mloop[i].v].color, (const uchar *)&col[i].r); + for (const int i : loops.index_range()) { + rgba_uchar_to_float(pPoint[loops[i].v].color, (const uchar *)&col[i].r); } } else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { @@ -1792,8 +1791,8 @@ struct DynamicPaintModifierApplyData { float (*vert_positions)[3]; const float (*vert_normals)[3]; - const MLoop *mloop; - const MPoly *mpoly; + blender::Span polys; + blender::Span loops; float (*fcolor)[4]; MLoopCol *mloopcol; @@ -1861,8 +1860,8 @@ static void dynamic_paint_apply_surface_vpaint_cb(void *__restrict userdata, const DynamicPaintModifierApplyData *data = static_cast( userdata); - const MLoop *mloop = data->mloop; - const MPoly *mpoly = data->mpoly; + const blender::Span polys = data->polys; + const blender::Span loops = data->loops; const DynamicPaintSurface *surface = data->surface; PaintPoint *pPoint = (PaintPoint *)surface->data->type_data; @@ -1871,9 +1870,9 @@ static void dynamic_paint_apply_surface_vpaint_cb(void *__restrict userdata, MLoopCol *mloopcol = data->mloopcol; MLoopCol *mloopcol_wet = data->mloopcol_wet; - for (int j = 0; j < mpoly[p_index].totloop; j++) { - const int l_index = mpoly[p_index].loopstart + j; - const int v_index = mloop[l_index].v; + for (int j = 0; j < polys[p_index].totloop; j++) { + const int l_index = polys[p_index].loopstart + j; + const int v_index = loops[l_index].v; /* save layer data to output layer */ /* apply color */ @@ -1930,10 +1929,8 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * /* vertex color paint */ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - const MLoop *mloop = BKE_mesh_loops(result); - const int totloop = result->totloop; - const MPoly *mpoly = BKE_mesh_polys(result); - const int totpoly = result->totpoly; + const blender::Span polys = result->polys(); + const blender::Span loops = result->loops(); /* paint is stored on dry and wet layers, so mix final color first */ float(*fcolor)[4] = static_cast( @@ -1963,7 +1960,7 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, nullptr, - totloop, + loops.size(), surface->output_name)); } @@ -1977,22 +1974,22 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, nullptr, - totloop, + loops.size(), surface->output_name2)); } data.ob = ob; - data.mloop = mloop; - data.mpoly = mpoly; + data.loops = loops; + data.polys = polys; data.mloopcol = mloopcol; data.mloopcol_wet = mloopcol_wet; { TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = (totpoly > 1000); + settings.use_threading = (polys.size() > 1000); BLI_task_parallel_range( - 0, totpoly, &data, dynamic_paint_apply_surface_vpaint_cb, &settings); + 0, polys.size(), &data, dynamic_paint_apply_surface_vpaint_cb, &settings); } MEM_freeN(fcolor); @@ -2197,7 +2194,7 @@ struct DynamicPaintCreateUVSurfaceData { const MLoopTri *mlooptri; const float (*mloopuv)[2]; - const MLoop *mloop; + blender::Span loops; int tottri; const Bounds2D *faceBB; @@ -2217,7 +2214,7 @@ static void dynamic_paint_create_uv_surface_direct_cb(void *__restrict userdata, const MLoopTri *mlooptri = data->mlooptri; const float(*mloopuv)[2] = data->mloopuv; - const MLoop *mloop = data->mloop; + const blender::Span loops = data->loops; const int tottri = data->tottri; const Bounds2D *faceBB = data->faceBB; @@ -2289,9 +2286,9 @@ static void dynamic_paint_create_uv_surface_direct_cb(void *__restrict userdata, tPoint->tri_index = i; /* save vertex indexes */ - tPoint->v1 = mloop[mlooptri[i].tri[0]].v; - tPoint->v2 = mloop[mlooptri[i].tri[1]].v; - tPoint->v3 = mloop[mlooptri[i].tri[2]].v; + tPoint->v1 = loops[mlooptri[i].tri[0]].v; + tPoint->v2 = loops[mlooptri[i].tri[1]].v; + tPoint->v3 = loops[mlooptri[i].tri[2]].v; sample = 5; /* make sure we exit sample loop as well */ break; @@ -2314,7 +2311,7 @@ static void dynamic_paint_create_uv_surface_neighbor_cb(void *__restrict userdat const MLoopTri *mlooptri = data->mlooptri; const float(*mloopuv)[2] = data->mloopuv; - const MLoop *mloop = data->mloop; + const blender::Span loops = data->loops; uint32_t *active_points = data->active_points; @@ -2379,9 +2376,9 @@ static void dynamic_paint_create_uv_surface_neighbor_cb(void *__restrict userdat } /* save vertex indexes */ - tPoint->v1 = mloop[mlooptri[i].tri[0]].v; - tPoint->v2 = mloop[mlooptri[i].tri[1]].v; - tPoint->v3 = mloop[mlooptri[i].tri[2]].v; + tPoint->v1 = loops[mlooptri[i].tri[0]].v; + tPoint->v2 = loops[mlooptri[i].tri[1]].v; + tPoint->v3 = loops[mlooptri[i].tri[2]].v; break; } @@ -2522,7 +2519,7 @@ static void dynamic_paint_find_island_border(const DynamicPaintCreateUVSurfaceDa int in_edge, int depth) { - const MLoop *mloop = data->mloop; + const blender::Span loops = data->loops; const MLoopTri *mlooptri = data->mlooptri; const float(*mloopuv)[2] = data->mloopuv; @@ -2560,8 +2557,8 @@ static void dynamic_paint_find_island_border(const DynamicPaintCreateUVSurfaceDa } /* Now find another face that is linked to that edge. */ - const int vert0 = mloop[loop_idx[(edge_idx + 0)]].v; - const int vert1 = mloop[loop_idx[(edge_idx + 1) % 3]].v; + const int vert0 = loops[loop_idx[(edge_idx + 0)]].v; + const int vert1 = loops[loop_idx[(edge_idx + 1) % 3]].v; /* Use a pre-computed vert-to-looptri mapping, * speeds up things a lot compared to looping over all looptri. */ @@ -2584,8 +2581,8 @@ static void dynamic_paint_find_island_border(const DynamicPaintCreateUVSurfaceDa /* Check edges for match, looping in the same order as the outer loop. */ for (int j = 0; j < 3; j++) { - const int overt0 = mloop[other_loop_idx[(j + 0)]].v; - const int overt1 = mloop[other_loop_idx[(j + 1) % 3]].v; + const int overt0 = loops[other_loop_idx[(j + 0)]].v; + const int overt1 = loops[other_loop_idx[(j + 1) % 3]].v; /* Allow for swapped vertex order */ if (overt0 == vert0 && overt1 == vert1) { @@ -2823,7 +2820,6 @@ int dynamicPaint_createUVSurface(Scene *scene, Vec3f *tempWeights = nullptr; const MLoopTri *mlooptri = nullptr; const float(*mloopuv)[2] = nullptr; - const MLoop *mloop = nullptr; Bounds2D *faceBB = nullptr; int *final_index; @@ -2838,7 +2834,7 @@ int dynamicPaint_createUVSurface(Scene *scene, return setError(canvas, N_("Cannot bake non-'image sequence' formats")); } - mloop = BKE_mesh_loops(mesh); + const blender::Span loops = mesh->loops(); mlooptri = BKE_mesh_runtime_looptri_ensure(mesh); const int tottri = BKE_mesh_runtime_looptri_len(mesh); @@ -2926,7 +2922,7 @@ int dynamicPaint_createUVSurface(Scene *scene, data.tempWeights = tempWeights; data.mlooptri = mlooptri; data.mloopuv = mloopuv; - data.mloop = mloop; + data.loops = loops; data.tottri = tottri; data.faceBB = faceBB; @@ -2985,7 +2981,7 @@ int dynamicPaint_createUVSurface(Scene *scene, mesh->totvert, mlooptri, tottri, - mloop, + loops.data(), mesh->totloop); int total_border = 0; @@ -3444,14 +3440,14 @@ static void mesh_tris_spherecast_dp(void *userdata, const BVHTreeFromMesh *data = (BVHTreeFromMesh *)userdata; const float(*positions)[3] = data->vert_positions; const MLoopTri *mlooptri = data->looptri; - const MLoop *mloop = data->loop; + const MLoop *loops = data->loop; const float *t0, *t1, *t2; float dist; - t0 = positions[mloop[mlooptri[index].tri[0]].v]; - t1 = positions[mloop[mlooptri[index].tri[1]].v]; - t2 = positions[mloop[mlooptri[index].tri[2]].v]; + t0 = positions[loops[mlooptri[index].tri[0]].v]; + t1 = positions[loops[mlooptri[index].tri[1]].v]; + t2 = positions[loops[mlooptri[index].tri[2]].v]; dist = bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2); @@ -3476,13 +3472,13 @@ static void mesh_tris_nearest_point_dp(void *userdata, const BVHTreeFromMesh *data = (BVHTreeFromMesh *)userdata; const float(*positions)[3] = data->vert_positions; const MLoopTri *mlooptri = data->looptri; - const MLoop *mloop = data->loop; + const MLoop *loops = data->loop; float nearest_tmp[3], dist_sq; const float *t0, *t1, *t2; - t0 = positions[mloop[mlooptri[index].tri[0]].v]; - t1 = positions[mloop[mlooptri[index].tri[1]].v]; - t2 = positions[mloop[mlooptri[index].tri[2]].v]; + t0 = positions[loops[mlooptri[index].tri[0]].v]; + t1 = positions[loops[mlooptri[index].tri[1]].v]; + t2 = positions[loops[mlooptri[index].tri[2]].v]; closest_on_tri_to_point_v3(nearest_tmp, co, t0, t1, t2); dist_sq = len_squared_v3v3(co, nearest_tmp); @@ -3915,7 +3911,7 @@ struct DynamicPaintPaintData { Mesh *mesh; const float (*positions)[3]; - const MLoop *mloop; + blender::Span loops; const MLoopTri *mlooptri; float brush_radius; const float *avg_brushNor; @@ -3949,7 +3945,7 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex(void *__restrict userdata, const int c_index = data->c_index; const float(*positions)[3] = data->positions; - const MLoop *mloop = data->mloop; + const blender::Span loops = data->loops; const MLoopTri *mlooptri = data->mlooptri; const float brush_radius = data->brush_radius; const float *avg_brushNor = data->avg_brushNor; @@ -4023,9 +4019,9 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex(void *__restrict userdata, /* For optimization sake, hit point normal isn't calculated in ray cast loop */ const int vtri[3] = { - int(mloop[mlooptri[hit.index].tri[0]].v), - int(mloop[mlooptri[hit.index].tri[1]].v), - int(mloop[mlooptri[hit.index].tri[2]].v), + int(loops[mlooptri[hit.index].tri[0]].v), + int(loops[mlooptri[hit.index].tri[1]].v), + int(loops[mlooptri[hit.index].tri[2]].v), }; float dot; @@ -4173,9 +4169,9 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex(void *__restrict userdata, float brushPointVelocity[3]; float velocity[3]; - const int v1 = mloop[mlooptri[hitTri].tri[0]].v; - const int v2 = mloop[mlooptri[hitTri].tri[1]].v; - const int v3 = mloop[mlooptri[hitTri].tri[2]].v; + const int v1 = loops[mlooptri[hitTri].tri[0]].v; + const int v2 = loops[mlooptri[hitTri].tri[1]].v; + const int v3 = loops[mlooptri[hitTri].tri[2]].v; /* calculate barycentric weights for hit point */ interp_weights_tri_v3(weights, positions[v1], positions[v2], positions[v3], hitCoord); @@ -4275,7 +4271,6 @@ static bool dynamicPaint_paintMesh(Depsgraph *depsgraph, Mesh *mesh = nullptr; Vec3f *brushVelocity = nullptr; const MLoopTri *mlooptri = nullptr; - const MLoop *mloop = nullptr; if (brush->flags & MOD_DPAINT_USES_VELOCITY) { dynamicPaint_brushMeshCalculateVelocity( @@ -4300,7 +4295,7 @@ static bool dynamicPaint_paintMesh(Depsgraph *depsgraph, float(*positions)[3] = BKE_mesh_vert_positions_for_write(mesh); const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh); mlooptri = BKE_mesh_runtime_looptri_ensure(mesh); - mloop = BKE_mesh_loops(mesh); + const blender::Span loops = mesh->loops(); numOfVerts = mesh->totvert; /* Transform collider vertices to global space @@ -4354,7 +4349,7 @@ static bool dynamicPaint_paintMesh(Depsgraph *depsgraph, data.c_index = c_index; data.mesh = mesh; data.positions = positions; - data.mloop = mloop; + data.loops = loops; data.mlooptri = mlooptri; data.brush_radius = brush_radius; data.avg_brushNor = avg_brushNor; diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 304ac5c63a0..8e054d4d35f 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -557,10 +557,11 @@ int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[], /* ...................................... */ /* Helper for calc_fcurve_* functions -> find first and last BezTriple to be used. */ -static short get_fcurve_end_keyframes(const FCurve *fcu, - BezTriple **first, - BezTriple **last, - const bool do_sel_only) +static bool get_fcurve_end_keyframes(const FCurve *fcu, + BezTriple **first, + BezTriple **last, + const bool do_sel_only, + const float range[2]) { bool found = false; @@ -573,11 +574,31 @@ static short get_fcurve_end_keyframes(const FCurve *fcu, return found; } + int first_index = 0; + int last_index = fcu->totvert - 1; + + if (range != NULL) { + /* If a range is passed in find the first and last keyframe within that range. */ + bool replace = false; + first_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[0], fcu->totvert, &replace); + last_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[1], fcu->totvert, &replace); + + /* If first and last index are the same, no keyframes were found in the range. */ + if (first_index == last_index) { + return found; + } + + /* The binary search returns an index where a keyframe would be inserted, + so it needs to be clamped to ensure it is in range of the array. */ + first_index = clamp_i(first_index, 0, fcu->totvert - 1); + last_index = clamp_i(last_index - 1, 0, fcu->totvert - 1); + } + /* Only include selected items? */ if (do_sel_only) { /* Find first selected. */ - BezTriple *bezt = fcu->bezt; - for (int i = 0; i < fcu->totvert; bezt++, i++) { + for (int i = first_index; i <= last_index; i++) { + BezTriple *bezt = &fcu->bezt[i]; if (BEZT_ISSEL_ANY(bezt)) { *first = bezt; found = true; @@ -586,8 +607,8 @@ static short get_fcurve_end_keyframes(const FCurve *fcu, } /* Find last selected. */ - bezt = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert); - for (int i = 0; i < fcu->totvert; bezt--, i++) { + for (int i = last_index; i >= first_index; i--) { + BezTriple *bezt = &fcu->bezt[i]; if (BEZT_ISSEL_ANY(bezt)) { *last = bezt; found = true; @@ -596,9 +617,8 @@ static short get_fcurve_end_keyframes(const FCurve *fcu, } } else { - /* Use the whole array. */ - *first = fcu->bezt; - *last = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert); + *first = &fcu->bezt[first_index]; + *last = &fcu->bezt[last_index]; found = true; } @@ -611,23 +631,25 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu, float *ymin, float *ymax, const bool do_sel_only, - const bool include_handles) + const bool include_handles, + const float range[2]) { float xminv = 999999999.0f, xmaxv = -999999999.0f; float yminv = 999999999.0f, ymaxv = -999999999.0f; bool foundvert = false; + const bool use_range = range != NULL; + if (fcu->totvert) { if (fcu->bezt) { - BezTriple *bezt_first = NULL, *bezt_last = NULL; if (xmin || xmax) { - /* Get endpoint keyframes. */ - foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only); + BezTriple *bezt_first = NULL, *bezt_last = NULL; + foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, range); if (bezt_first) { BLI_assert(bezt_last != NULL); - + foundvert = true; if (include_handles) { xminv = min_fff(xminv, bezt_first->vec[0][0], bezt_first->vec[1][0]); xmaxv = max_fff(xmaxv, bezt_last->vec[1][0], bezt_last->vec[2][0]); @@ -645,6 +667,9 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu, int i; for (bezt = fcu->bezt, i = 0; i < fcu->totvert; prevbezt = bezt, bezt++, i++) { + if (use_range && (bezt->vec[1][0] < range[0] || bezt->vec[1][0] > range[1])) { + continue; + } if ((do_sel_only == false) || BEZT_ISSEL_ANY(bezt)) { /* Keyframe itself. */ yminv = min_ff(yminv, bezt->vec[1][1]); @@ -717,10 +742,10 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu, } if (xmin) { - *xmin = 0.0f; + *xmin = use_range ? range[0] : 0.0f; } if (xmax) { - *xmax = 1.0f; + *xmax = use_range ? range[1] : 1.0f; } if (ymin) { @@ -745,7 +770,7 @@ bool BKE_fcurve_calc_range( BezTriple *bezt_first = NULL, *bezt_last = NULL; /* Get endpoint keyframes. */ - get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only); + get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, NULL); if (bezt_first) { BLI_assert(bezt_last != NULL); diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index e775a2a00a9..7401546c19d 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -375,7 +375,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_FLOAT3, CD_PROP_FLOAT3, BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, point_access, make_array_read_attribute, @@ -387,7 +386,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_FLOAT, CD_PROP_FLOAT, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, @@ -399,7 +397,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_INT32, CD_PROP_INT32, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, @@ -411,7 +408,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_FLOAT, CD_PROP_FLOAT, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, @@ -423,7 +419,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_FLOAT3, CD_PROP_FLOAT3, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, @@ -435,7 +430,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_FLOAT3, CD_PROP_FLOAT3, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, @@ -453,7 +447,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, @@ -466,7 +459,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, @@ -479,7 +471,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_FLOAT, CD_PROP_FLOAT, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, @@ -495,7 +486,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, @@ -514,7 +504,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, @@ -533,7 +522,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, @@ -552,7 +540,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_INT8, CD_PROP_INT8, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, @@ -569,7 +556,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_INT32, CD_PROP_INT32, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, @@ -582,7 +568,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() CD_PROP_BOOL, CD_PROP_BOOL, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, curve_access, make_array_read_attribute, diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 25143bf20a0..0c8c6620668 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -129,7 +129,7 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider public: InstancePositionAttributeProvider() : BuiltinAttributeProvider( - "position", ATTR_DOMAIN_INSTANCE, CD_PROP_FLOAT3, NonCreatable, Writable, NonDeletable) + "position", ATTR_DOMAIN_INSTANCE, CD_PROP_FLOAT3, NonCreatable, NonDeletable) { } @@ -200,7 +200,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances() CD_PROP_INT32, CD_PROP_INT32, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, instance_custom_data_access, make_array_read_attribute, diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index bdb1b0edf8b..44697ca2a30 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -1137,48 +1137,6 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { } }; -/** - * This provider makes face normals available as a read-only float3 attribute. - */ -class NormalAttributeProvider final : public BuiltinAttributeProvider { - public: - NormalAttributeProvider() - : BuiltinAttributeProvider( - "normal", ATTR_DOMAIN_FACE, CD_PROP_FLOAT3, NonCreatable, Readonly, NonDeletable) - { - } - - GVArray try_get_for_read(const void *owner) const final - { - const Mesh *mesh = static_cast(owner); - if (mesh == nullptr || mesh->totpoly == 0) { - return {}; - } - return VArray::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly}); - } - - GAttributeWriter try_get_for_write(void * /*owner*/) const final - { - return {}; - } - - bool try_delete(void * /*owner*/) const final - { - return false; - } - - bool try_create(void * /*owner*/, const AttributeInit & /*initializer*/) const final - { - return false; - } - - bool exists(const void *owner) const final - { - const Mesh *mesh = static_cast(owner); - return mesh->totpoly != 0; - } -}; - /** * In this function all the attribute providers for a mesh component are created. Most data in this * function is statically allocated, because it does not change over time. @@ -1222,21 +1180,17 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_FLOAT3, CD_PROP_FLOAT3, BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, point_access, make_array_read_attribute, make_array_write_attribute, tag_component_positions_changed); - static NormalAttributeProvider normal; - static BuiltinCustomDataLayerProvider id("id", ATTR_DOMAIN_POINT, CD_PROP_INT32, CD_PROP_INT32, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, @@ -1255,7 +1209,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_INT32, CD_PROP_INT32, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, face_access, make_array_read_attribute, @@ -1269,7 +1222,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_BOOL, CD_MPOLY, BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, face_access, make_derived_read_attribute, @@ -1281,7 +1233,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_BOOL, CD_PROP_BOOL, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, edge_access, make_array_read_attribute, @@ -1294,7 +1245,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_FLOAT, CD_CREASE, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, edge_access, make_array_read_attribute, @@ -1308,7 +1258,7 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() static CustomDataAttributeProvider face_custom_data(ATTR_DOMAIN_FACE, face_access); return ComponentAttributeProviders( - {&position, &id, &material_index, &shade_smooth, &sharp_edge, &normal, &crease}, + {&position, &id, &material_index, &shade_smooth, &sharp_edge, &crease}, {&corner_custom_data, &vertex_groups, &point_custom_data, diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 71484bbd5f0..41966fe868a 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -142,7 +142,6 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() CD_PROP_FLOAT3, CD_PROP_FLOAT3, BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, point_access, make_array_read_attribute, @@ -153,7 +152,6 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() CD_PROP_FLOAT, CD_PROP_FLOAT, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, @@ -164,7 +162,6 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() CD_PROP_INT32, CD_PROP_INT32, BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::Deletable, point_access, make_array_read_attribute, diff --git a/source/blender/blenkernel/intern/key.cc b/source/blender/blenkernel/intern/key.cc index 2c25ec0bdeb..a4f3fbf2de3 100644 --- a/source/blender/blenkernel/intern/key.cc +++ b/source/blender/blenkernel/intern/key.cc @@ -2233,9 +2233,9 @@ void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb, float(*positions)[3] = static_cast(MEM_dupallocN(BKE_mesh_vert_positions(mesh))); BKE_keyblock_convert_to_mesh(kb, positions, mesh->totvert); - const MEdge *edges = BKE_mesh_edges(mesh); - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); + const blender::Span edges = mesh->edges(); + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); const bool loop_normals_needed = r_loop_normals != nullptr; const bool vert_normals_needed = r_vert_normals != nullptr || loop_normals_needed; @@ -2258,35 +2258,40 @@ void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb, } if (poly_normals_needed) { - BKE_mesh_calc_normals_poly( - positions, mesh->totvert, loops, mesh->totloop, polys, mesh->totpoly, poly_normals); + BKE_mesh_calc_normals_poly(positions, + mesh->totvert, + loops.data(), + loops.size(), + polys.data(), + polys.size(), + poly_normals); } if (vert_normals_needed) { BKE_mesh_calc_normals_poly_and_vertex(positions, mesh->totvert, - loops, - mesh->totloop, - polys, - mesh->totpoly, + loops.data(), + loops.size(), + polys.data(), + polys.size(), poly_normals, vert_normals); } if (loop_normals_needed) { short(*clnors)[2] = static_cast(CustomData_get_layer_for_write( - &mesh->ldata, CD_CUSTOMLOOPNORMAL, mesh->totloop)); /* May be nullptr. */ + &mesh->ldata, CD_CUSTOMLOOPNORMAL, loops.size())); /* May be nullptr. */ const bool *sharp_edges = static_cast( CustomData_get_layer_named(&mesh->edata, CD_PROP_BOOL, "sharp_edge")); BKE_mesh_normals_loop_split(positions, vert_normals, mesh->totvert, - edges, + edges.data(), mesh->totedge, - loops, + loops.data(), r_loop_normals, - mesh->totloop, - polys, + loops.size(), + polys.data(), poly_normals, - mesh->totpoly, + polys.size(), (mesh->flag & ME_AUTOSMOOTH) != 0, mesh->smoothresh, sharp_edges, diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 5b4c4edd0a2..f1f6a9a97bc 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -17,7 +17,6 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" -#include "BLI_bit_vector.hh" #include "BLI_bounds.hh" #include "BLI_edgehash.h" #include "BLI_endian_switch.h" @@ -66,7 +65,6 @@ #include "BLO_read_write.h" -using blender::BitVector; using blender::float3; using blender::MutableSpan; using blender::Span; diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc index d6f40a472de..8cab5a4dd9a 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.cc +++ b/source/blender/blenkernel/intern/mesh_evaluate.cc @@ -329,21 +329,19 @@ bool BKE_mesh_center_bounds(const Mesh *me, float r_cent[3]) bool BKE_mesh_center_of_surface(const Mesh *me, float r_cent[3]) { - int i = me->totpoly; - const MPoly *mpoly; float poly_area; float total_area = 0.0f; float poly_cent[3]; const float(*positions)[3] = BKE_mesh_vert_positions(me); - const MPoly *polys = BKE_mesh_polys(me); - const MLoop *loops = BKE_mesh_loops(me); + const blender::Span polys = me->polys(); + const blender::Span loops = me->loops(); zero_v3(r_cent); /* calculate a weighted average of polygon centroids */ - for (mpoly = polys; i--; mpoly++) { + for (const int i : polys.index_range()) { poly_area = mesh_calc_poly_area_centroid( - mpoly, loops + mpoly->loopstart, positions, poly_cent); + &polys[i], &loops[polys[i].loopstart], positions, poly_cent); madd_v3_v3fl(r_cent, poly_cent, poly_area); total_area += poly_area; @@ -363,14 +361,12 @@ bool BKE_mesh_center_of_surface(const Mesh *me, float r_cent[3]) bool BKE_mesh_center_of_volume(const Mesh *me, float r_cent[3]) { - int i = me->totpoly; - const MPoly *mpoly; float poly_volume; float total_volume = 0.0f; float poly_cent[3]; const Span positions = me->vert_positions(); - const MPoly *polys = BKE_mesh_polys(me); - const MLoop *loops = BKE_mesh_loops(me); + const blender::Span polys = me->polys(); + const blender::Span loops = me->loops(); /* Use an initial center to avoid numeric instability of geometry far away from the center. */ float init_cent[3]; @@ -379,9 +375,9 @@ bool BKE_mesh_center_of_volume(const Mesh *me, float r_cent[3]) zero_v3(r_cent); /* calculate a weighted average of polyhedron centroids */ - for (mpoly = polys; i--; mpoly++) { + for (const int i : polys.index_range()) { poly_volume = mesh_calc_poly_volume_centroid_with_reference_center( - mpoly, loops + mpoly->loopstart, positions, init_cent, poly_cent); + &polys[i], &loops[polys[i].loopstart], positions, init_cent, poly_cent); /* poly_cent is already volume-weighted, so no need to multiply by the volume */ add_v3_v3(r_cent, poly_cent); diff --git a/source/blender/blenkernel/intern/mesh_iterators.cc b/source/blender/blenkernel/intern/mesh_iterators.cc index 45ad8f2b626..7d6a9d58a79 100644 --- a/source/blender/blenkernel/intern/mesh_iterators.cc +++ b/source/blender/blenkernel/intern/mesh_iterators.cc @@ -121,21 +121,22 @@ void BKE_mesh_foreach_mapped_edge( } else { const float(*positions)[3] = BKE_mesh_vert_positions(mesh); - const MEdge *med = BKE_mesh_edges(mesh); + const blender::Span edges = mesh->edges(); const int *index = static_cast(CustomData_get_layer(&mesh->edata, CD_ORIGINDEX)); if (index) { - for (int i = 0; i < mesh->totedge; i++, med++) { + for (const int i : edges.index_range()) { + const int orig = *index++; if (orig == ORIGINDEX_NONE) { continue; } - func(userData, orig, positions[med->v1], positions[med->v2]); + func(userData, orig, positions[edges[i].v1], positions[edges[i].v2]); } } else if (mesh->totedge == tot_edges) { - for (int i = 0; i < mesh->totedge; i++, med++) { - func(userData, i, positions[med->v1], positions[med->v2]); + for (const int i : edges.index_range()) { + func(userData, i, positions[edges[i].v1], positions[edges[i].v2]); } } } @@ -191,34 +192,35 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh, nullptr; const float(*positions)[3] = BKE_mesh_vert_positions(mesh); - const MLoop *ml = BKE_mesh_loops(mesh); - const MPoly *mp = BKE_mesh_polys(mesh); + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); const int *v_index = static_cast( CustomData_get_layer(&mesh->vdata, CD_ORIGINDEX)); const int *f_index = static_cast( CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX)); - int p_idx, i; if (v_index || f_index) { - for (p_idx = 0; p_idx < mesh->totpoly; p_idx++, mp++) { - for (i = 0; i < mp->totloop; i++, ml++) { - const int v_idx = v_index ? v_index[ml->v] : ml->v; - const int f_idx = f_index ? f_index[p_idx] : p_idx; + for (const int poly_i : polys.index_range()) { + for (const int loop_i : + blender::IndexRange(polys[poly_i].loopstart, polys[poly_i].totloop)) { + const int v_idx = v_index ? v_index[loops[loop_i].v] : loops[loop_i].v; + const int f_idx = f_index ? f_index[poly_i] : poly_i; const float *no = loop_normals ? *loop_normals++ : nullptr; if (ELEM(ORIGINDEX_NONE, v_idx, f_idx)) { continue; } - func(userData, v_idx, f_idx, positions[ml->v], no); + func(userData, v_idx, f_idx, positions[loops[loop_i].v], no); } } } else { - for (p_idx = 0; p_idx < mesh->totpoly; p_idx++, mp++) { - for (i = 0; i < mp->totloop; i++, ml++) { - const int v_idx = ml->v; - const int f_idx = p_idx; + for (const int poly_i : polys.index_range()) { + for (const int loop_i : + blender::IndexRange(polys[poly_i].loopstart, polys[poly_i].totloop)) { + const int v_idx = loops[loop_i].v; + const int f_idx = poly_i; const float *no = loop_normals ? *loop_normals++ : nullptr; - func(userData, v_idx, f_idx, positions[ml->v], no); + func(userData, v_idx, f_idx, positions[loops[loop_i].v], no); } } } @@ -266,35 +268,32 @@ void BKE_mesh_foreach_mapped_face_center( } else { const float(*positions)[3] = BKE_mesh_vert_positions(mesh); - const MPoly *mp = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); - const MLoop *ml; + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); float _no_buf[3]; float *no = (flag & MESH_FOREACH_USE_NORMAL) ? _no_buf : nullptr; const int *index = static_cast(CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX)); if (index) { - for (int i = 0; i < mesh->totpoly; i++, mp++) { + for (const int i : polys.index_range()) { const int orig = *index++; if (orig == ORIGINDEX_NONE) { continue; } float cent[3]; - ml = &loops[mp->loopstart]; - BKE_mesh_calc_poly_center(mp, ml, positions, cent); + BKE_mesh_calc_poly_center(&polys[i], &loops[polys[i].loopstart], positions, cent); if (flag & MESH_FOREACH_USE_NORMAL) { - BKE_mesh_calc_poly_normal(mp, ml, positions, no); + BKE_mesh_calc_poly_normal(&polys[i], &loops[polys[i].loopstart], positions, no); } func(userData, orig, cent, no); } } else { - for (int i = 0; i < mesh->totpoly; i++, mp++) { + for (const int i : polys.index_range()) { float cent[3]; - ml = &loops[mp->loopstart]; - BKE_mesh_calc_poly_center(mp, ml, positions, cent); + BKE_mesh_calc_poly_center(&polys[i], &loops[polys[i].loopstart], positions, cent); if (flag & MESH_FOREACH_USE_NORMAL) { - BKE_mesh_calc_poly_normal(mp, ml, positions, no); + BKE_mesh_calc_poly_normal(&polys[i], &loops[polys[i].loopstart], positions, no); } func(userData, i, cent, no); } @@ -309,41 +308,38 @@ void BKE_mesh_foreach_mapped_subdiv_face_center( MeshForeachFlag flag) { const float(*positions)[3] = BKE_mesh_vert_positions(mesh); - const MPoly *mp = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); - const MLoop *ml; + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); const float(*vert_normals)[3] = (flag & MESH_FOREACH_USE_NORMAL) ? BKE_mesh_vertex_normals_ensure(mesh) : nullptr; const int *index = static_cast(CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX)); - const blender::BitVector<> &facedot_tags = mesh->runtime->subsurf_face_dot_tags; + const blender::BitSpan facedot_tags = mesh->runtime->subsurf_face_dot_tags; if (index) { - for (int i = 0; i < mesh->totpoly; i++, mp++) { + for (const int i : polys.index_range()) { const int orig = *index++; if (orig == ORIGINDEX_NONE) { continue; } - ml = &loops[mp->loopstart]; - for (int j = 0; j < mp->totloop; j++, ml++) { - if (facedot_tags[ml->v]) { + for (const MLoop &loop : loops.slice(polys[i].loopstart, polys[i].totloop)) { + if (facedot_tags[loop.v]) { func(userData, orig, - positions[ml->v], - (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[ml->v] : nullptr); + positions[loop.v], + (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[loop.v] : nullptr); } } } } else { - for (int i = 0; i < mesh->totpoly; i++, mp++) { - ml = &loops[mp->loopstart]; - for (int j = 0; j < mp->totloop; j++, ml++) { - if (facedot_tags[ml->v]) { + for (const int i : polys.index_range()) { + for (const MLoop &loop : loops.slice(polys[i].loopstart, polys[i].totloop)) { + if (facedot_tags[loop.v]) { func(userData, i, - positions[ml->v], - (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[ml->v] : nullptr); + positions[loop.v], + (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[loop.v] : nullptr); } } } diff --git a/source/blender/blenkernel/intern/mesh_merge.c b/source/blender/blenkernel/intern/mesh_merge.c deleted file mode 100644 index fbc4ac3d208..00000000000 --- a/source/blender/blenkernel/intern/mesh_merge.c +++ /dev/null @@ -1,644 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ - -/** \file - * \ingroup bke - */ -#include /* for memcpy */ - -#include "MEM_guardedalloc.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BLI_bitmap.h" -#include "BLI_edgehash.h" -#include "BLI_ghash.h" -#include "BLI_math_vector.h" -#include "BLI_utildefines.h" -#include "BLI_utildefines_stack.h" - -#include "BKE_customdata.h" -#include "BKE_lib_id.h" -#include "BKE_mesh.h" -#include "BKE_mesh_mapping.h" - -/** - * Poly compare with vtargetmap - * Function used by #BKE_mesh_merge_verts. - * The function compares poly_source after applying vtargetmap, with poly_target. - * The two polys are identical if they share the same vertices in the same order, - * or in reverse order, but starting position loopstart may be different. - * The function is called with direct_reverse=1 for same order (i.e. same normal), - * and may be called again with direct_reverse=-1 for reverse order. - * \return 1 if polys are identical, 0 if polys are different. - */ -static int cddm_poly_compare(const MLoop *mloop_array, - const MPoly *mpoly_source, - const MPoly *mpoly_target, - const int *vtargetmap, - const int direct_reverse) -{ - int vert_source, first_vert_source, vert_target; - int i_loop_source; - int i_loop_target, i_loop_target_start, i_loop_target_offset, i_loop_target_adjusted; - bool compare_completed = false; - bool same_loops = false; - - const MLoop *mloop_source, *mloop_target; - - BLI_assert(ELEM(direct_reverse, 1, -1)); - - i_loop_source = 0; - mloop_source = mloop_array + mpoly_source->loopstart; - vert_source = mloop_source->v; - - if (vtargetmap[vert_source] != -1) { - vert_source = vtargetmap[vert_source]; - } - else { - /* All source loop vertices should be mapped */ - BLI_assert(false); - } - - /* Find same vertex within mpoly_target's loops */ - mloop_target = mloop_array + mpoly_target->loopstart; - for (i_loop_target = 0; i_loop_target < mpoly_target->totloop; i_loop_target++, mloop_target++) { - if (mloop_target->v == vert_source) { - break; - } - } - - /* If same vertex not found, then polys cannot be equal */ - if (i_loop_target >= mpoly_target->totloop) { - return false; - } - - /* Now mloop_source and m_loop_target have one identical vertex */ - /* mloop_source is at position 0, while m_loop_target has advanced to find identical vertex */ - /* Go around the loop and check that all vertices match in same order */ - /* Skipping source loops when consecutive source vertices are mapped to same target vertex */ - - i_loop_target_start = i_loop_target; - i_loop_target_offset = 0; - first_vert_source = vert_source; - - compare_completed = false; - same_loops = false; - - while (!compare_completed) { - - vert_target = mloop_target->v; - - /* First advance i_loop_source, until it points to different vertex, after mapping applied */ - do { - i_loop_source++; - - if (i_loop_source == mpoly_source->totloop) { - /* End of loops for source, must match end of loop for target. */ - if (i_loop_target_offset == mpoly_target->totloop - 1) { - compare_completed = true; - same_loops = true; - break; /* Polys are identical */ - } - - compare_completed = true; - same_loops = false; - break; /* Polys are different */ - } - - mloop_source++; - vert_source = mloop_source->v; - - if (vtargetmap[vert_source] != -1) { - vert_source = vtargetmap[vert_source]; - } - else { - /* All source loop vertices should be mapped */ - BLI_assert(false); - } - - } while (vert_source == vert_target); - - if (compare_completed) { - break; - } - - /* Now advance i_loop_target as well */ - i_loop_target_offset++; - - if (i_loop_target_offset == mpoly_target->totloop) { - /* End of loops for target only, that means no match */ - /* except if all remaining source vertices are mapped to first target */ - for (; i_loop_source < mpoly_source->totloop; i_loop_source++, mloop_source++) { - vert_source = vtargetmap[mloop_source->v]; - if (vert_source != first_vert_source) { - compare_completed = true; - same_loops = false; - break; - } - } - if (!compare_completed) { - same_loops = true; - } - break; - } - - /* Adjust i_loop_target for cycling around and for direct/reverse order - * defined by delta = +1 or -1 */ - i_loop_target_adjusted = (i_loop_target_start + direct_reverse * i_loop_target_offset) % - mpoly_target->totloop; - if (i_loop_target_adjusted < 0) { - i_loop_target_adjusted += mpoly_target->totloop; - } - mloop_target = mloop_array + mpoly_target->loopstart + i_loop_target_adjusted; - vert_target = mloop_target->v; - - if (vert_target != vert_source) { - same_loops = false; /* Polys are different */ - break; - } - } - return same_loops; -} - -/* Utility stuff for using GHash with polys, used by vertex merging. */ - -typedef struct PolyKey { - int poly_index; /* index of the MPoly within the derived mesh */ - int totloops; /* number of loops in the poly */ - uint hash_sum; /* Sum of all vertices indices */ - uint hash_xor; /* Xor of all vertices indices */ -} PolyKey; - -static uint poly_gset_hash_fn(const void *key) -{ - const PolyKey *pk = key; - return pk->hash_sum; -} - -static bool poly_gset_compare_fn(const void *k1, const void *k2) -{ - const PolyKey *pk1 = k1; - const PolyKey *pk2 = k2; - if ((pk1->hash_sum == pk2->hash_sum) && (pk1->hash_xor == pk2->hash_xor) && - (pk1->totloops == pk2->totloops)) { - /* Equality - note that this does not mean equality of polys */ - return false; - } - - return true; -} - -Mesh *BKE_mesh_merge_verts(Mesh *mesh, - const int *vtargetmap, - const int tot_vtargetmap, - const int merge_mode) -{ - /* This was commented out back in 2013, see commit f45d8827bafe6b9eaf9de42f4054e9d84a21955d. */ - // #define USE_LOOPS - - Mesh *result = NULL; - - const int totvert = mesh->totvert; - const int totedge = mesh->totedge; - const int totloop = mesh->totloop; - const int totpoly = mesh->totpoly; - const MEdge *src_edges = BKE_mesh_edges(mesh); - const MPoly *src_polys = BKE_mesh_polys(mesh); - const MLoop *src_loops = BKE_mesh_loops(mesh); - - const int totvert_final = totvert - tot_vtargetmap; - - int *oldv = MEM_malloc_arrayN(totvert_final, sizeof(*oldv), __func__); - int *newv = MEM_malloc_arrayN(totvert, sizeof(*newv), __func__); - STACK_DECLARE(oldv); - - /* NOTE: create (totedge + totloop) elements because partially invalid polys due to merge may - * require generating new edges, and while in 99% cases we'll still end with less final edges - * than totedge, cases can be forged that would end requiring more. */ - const MEdge *med; - MEdge *medge = MEM_malloc_arrayN((totedge + totloop), sizeof(*medge), __func__); - int *olde = MEM_malloc_arrayN((totedge + totloop), sizeof(*olde), __func__); - int *newe = MEM_malloc_arrayN((totedge + totloop), sizeof(*newe), __func__); - STACK_DECLARE(medge); - STACK_DECLARE(olde); - - const MLoop *ml; - MLoop *mloop = MEM_malloc_arrayN(totloop, sizeof(*mloop), __func__); - int *oldl = MEM_malloc_arrayN(totloop, sizeof(*oldl), __func__); -#ifdef USE_LOOPS - int *newl = MEM_malloc_arrayN(totloop, sizeof(*newl), __func__); -#endif - STACK_DECLARE(mloop); - STACK_DECLARE(oldl); - - const MPoly *mp; - MPoly *mpoly = MEM_malloc_arrayN(totpoly, sizeof(*medge), __func__); - int *oldp = MEM_malloc_arrayN(totpoly, sizeof(*oldp), __func__); - STACK_DECLARE(mpoly); - STACK_DECLARE(oldp); - - EdgeHash *ehash = BLI_edgehash_new_ex(__func__, totedge); - - int i, j, c; - - PolyKey *poly_keys; - GSet *poly_gset = NULL; - MeshElemMap *poly_map = NULL; - int *poly_map_mem = NULL; - - STACK_INIT(oldv, totvert_final); - STACK_INIT(olde, totedge); - STACK_INIT(oldl, totloop); - STACK_INIT(oldp, totpoly); - - STACK_INIT(medge, totedge); - STACK_INIT(mloop, totloop); - STACK_INIT(mpoly, totpoly); - - /* fill newv with destination vertex indices */ - c = 0; - for (i = 0; i < totvert; i++) { - if (vtargetmap[i] == -1) { - STACK_PUSH(oldv, i); - newv[i] = c++; - } - else { - /* dummy value */ - newv[i] = 0; - } - } - - /* now link target vertices to destination indices */ - for (i = 0; i < totvert; i++) { - if (vtargetmap[i] != -1) { - newv[i] = newv[vtargetmap[i]]; - } - } - - /* Don't remap vertices in cddm->mloop, because we need to know the original - * indices in order to skip faces with all vertices merged. - * The "update loop indices..." section further down remaps vertices in mloop. - */ - - /* now go through and fix edges and faces */ - med = src_edges; - c = 0; - for (i = 0; i < totedge; i++, med++) { - const uint v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1; - const uint v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2; - if (LIKELY(v1 != v2)) { - void **val_p; - - if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) { - newe[i] = POINTER_AS_INT(*val_p); - } - else { - STACK_PUSH(olde, i); - STACK_PUSH(medge, *med); - newe[i] = c; - *val_p = POINTER_FROM_INT(c); - c++; - } - } - else { - newe[i] = -1; - } - } - - if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_EQUAL) { - /* In this mode, we need to determine, whenever a poly' vertices are all mapped */ - /* if the targets already make up a poly, in which case the new poly is dropped */ - /* This poly equality check is rather complex. - * We use a BLI_ghash to speed it up with a first level check */ - PolyKey *mpgh; - poly_keys = MEM_malloc_arrayN(totpoly, sizeof(PolyKey), __func__); - poly_gset = BLI_gset_new_ex(poly_gset_hash_fn, poly_gset_compare_fn, __func__, totpoly); - /* Duplicates allowed because our compare function is not pure equality */ - BLI_gset_flag_set(poly_gset, GHASH_FLAG_ALLOW_DUPES); - - mp = src_polys; - mpgh = poly_keys; - for (i = 0; i < totpoly; i++, mp++, mpgh++) { - mpgh->poly_index = i; - mpgh->totloops = mp->totloop; - ml = src_loops + mp->loopstart; - mpgh->hash_sum = mpgh->hash_xor = 0; - for (j = 0; j < mp->totloop; j++, ml++) { - mpgh->hash_sum += ml->v; - mpgh->hash_xor ^= ml->v; - } - BLI_gset_insert(poly_gset, mpgh); - } - - /* Can we optimize by reusing an old `pmap`? How do we know an old `pmap` is stale? */ - /* When called by `MOD_array.c` the `cddm` has just been created, so it has no valid `pmap`. */ - BKE_mesh_vert_poly_map_create( - &poly_map, &poly_map_mem, src_polys, src_loops, totvert, totpoly, totloop); - } /* done preparing for fast poly compare */ - - BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__); - - mp = src_polys; - for (i = 0; i < totpoly; i++, mp++) { - MPoly *mp_new; - - ml = src_loops + mp->loopstart; - - /* check faces with all vertices merged */ - bool all_verts_merged = true; - - for (j = 0; j < mp->totloop; j++, ml++) { - if (vtargetmap[ml->v] == -1) { - all_verts_merged = false; - /* This will be used to check for poly using several time the same vert. */ - BLI_BITMAP_DISABLE(vert_tag, ml->v); - } - else { - /* This will be used to check for poly using several time the same vert. */ - BLI_BITMAP_DISABLE(vert_tag, vtargetmap[ml->v]); - } - } - - if (UNLIKELY(all_verts_merged)) { - if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_MAPPED) { - /* In this mode, all vertices merged is enough to dump face */ - continue; - } - if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_EQUAL) { - /* Additional condition for face dump: target vertices must make up an identical face. - * The test has 2 steps: - * 1) first step is fast `ghash` lookup, but not fail-proof. - * 2) second step is thorough but more costly poly compare. */ - int i_poly, v_target; - bool found = false; - PolyKey pkey; - - /* Use poly_gset for fast (although not 100% certain) identification of same poly */ - /* First, make up a poly_summary structure */ - ml = src_loops + mp->loopstart; - pkey.hash_sum = pkey.hash_xor = 0; - pkey.totloops = 0; - for (j = 0; j < mp->totloop; j++, ml++) { - v_target = vtargetmap[ml->v]; /* Cannot be -1, they are all mapped */ - pkey.hash_sum += v_target; - pkey.hash_xor ^= v_target; - pkey.totloops++; - } - if (BLI_gset_haskey(poly_gset, &pkey)) { - - /* There might be a poly that matches this one. - * We could just leave it there and say there is, and do a "continue". - * ... but we are checking whether there is an exact poly match. - * It's not so costly in terms of CPU since it's very rare, just a lot of complex code. - */ - - /* Consider current loop again */ - ml = src_loops + mp->loopstart; - /* Consider the target of the loop's first vert */ - v_target = vtargetmap[ml->v]; - /* Now see if v_target belongs to a poly that shares all vertices with source poly, - * in same order, or reverse order */ - - for (i_poly = 0; i_poly < poly_map[v_target].count; i_poly++) { - const MPoly *target_poly = src_polys + *(poly_map[v_target].indices + i_poly); - - if (cddm_poly_compare(src_loops, mp, target_poly, vtargetmap, +1) || - cddm_poly_compare(src_loops, mp, target_poly, vtargetmap, -1)) { - found = true; - break; - } - } - if (found) { - /* Current poly's vertices are mapped to a poly that is strictly identical */ - /* Current poly is dumped */ - continue; - } - } - } - } - - /* Here either the poly's vertices were not all merged - * or they were all merged, but targets do not make up an identical poly, - * the poly is retained. - */ - ml = src_loops + mp->loopstart; - - c = 0; - MLoop *last_valid_ml = NULL; - MLoop *first_valid_ml = NULL; - bool need_edge_from_last_valid_ml = false; - bool need_edge_to_first_valid_ml = false; - int created_edges = 0; - for (j = 0; j < mp->totloop; j++, ml++) { - const uint mlv = (vtargetmap[ml->v] != -1) ? vtargetmap[ml->v] : ml->v; -#ifndef NDEBUG - { - const MLoop *next_ml = src_loops + mp->loopstart + ((j + 1) % mp->totloop); - uint next_mlv = (vtargetmap[next_ml->v] != -1) ? vtargetmap[next_ml->v] : next_ml->v; - med = src_edges + ml->e; - uint v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1; - uint v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2; - BLI_assert((mlv == v1 && next_mlv == v2) || (mlv == v2 && next_mlv == v1)); - } -#endif - /* A loop is only valid if its matching edge is, - * and it's not reusing a vertex already used by this poly. */ - if (LIKELY((newe[ml->e] != -1) && !BLI_BITMAP_TEST(vert_tag, mlv))) { - BLI_BITMAP_ENABLE(vert_tag, mlv); - - if (UNLIKELY(last_valid_ml != NULL && need_edge_from_last_valid_ml)) { - /* We need to create a new edge between last valid loop and this one! */ - void **val_p; - - uint v1 = (vtargetmap[last_valid_ml->v] != -1) ? vtargetmap[last_valid_ml->v] : - last_valid_ml->v; - uint v2 = mlv; - BLI_assert(v1 != v2); - if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) { - last_valid_ml->e = POINTER_AS_INT(*val_p); - } - else { - const int new_eidx = STACK_SIZE(medge); - STACK_PUSH(olde, olde[last_valid_ml->e]); - STACK_PUSH(medge, src_edges[last_valid_ml->e]); - medge[new_eidx].v1 = last_valid_ml->v; - medge[new_eidx].v2 = ml->v; - /* DO NOT change newe mapping, - * could break actual values due to some deleted original edges. */ - *val_p = POINTER_FROM_INT(new_eidx); - created_edges++; - - last_valid_ml->e = new_eidx; - } - need_edge_from_last_valid_ml = false; - } - -#ifdef USE_LOOPS - newl[j + mp->loopstart] = STACK_SIZE(mloop); -#endif - STACK_PUSH(oldl, j + mp->loopstart); - last_valid_ml = STACK_PUSH_RET_PTR(mloop); - *last_valid_ml = *ml; - if (first_valid_ml == NULL) { - first_valid_ml = last_valid_ml; - } - c++; - - /* We absolutely HAVE to handle edge index remapping here, otherwise potential newly - * created edges in that part of code make remapping later totally unreliable. */ - BLI_assert(newe[ml->e] != -1); - last_valid_ml->e = newe[ml->e]; - } - else { - if (last_valid_ml != NULL) { - need_edge_from_last_valid_ml = true; - } - else { - need_edge_to_first_valid_ml = true; - } - } - } - if (UNLIKELY(last_valid_ml != NULL && !ELEM(first_valid_ml, NULL, last_valid_ml) && - (need_edge_to_first_valid_ml || need_edge_from_last_valid_ml))) { - /* We need to create a new edge between last valid loop and first valid one! */ - void **val_p; - - uint v1 = (vtargetmap[last_valid_ml->v] != -1) ? vtargetmap[last_valid_ml->v] : - last_valid_ml->v; - uint v2 = (vtargetmap[first_valid_ml->v] != -1) ? vtargetmap[first_valid_ml->v] : - first_valid_ml->v; - BLI_assert(v1 != v2); - if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) { - last_valid_ml->e = POINTER_AS_INT(*val_p); - } - else { - const int new_eidx = STACK_SIZE(medge); - STACK_PUSH(olde, olde[last_valid_ml->e]); - STACK_PUSH(medge, src_edges[last_valid_ml->e]); - medge[new_eidx].v1 = last_valid_ml->v; - medge[new_eidx].v2 = first_valid_ml->v; - /* DO NOT change newe mapping, - * could break actual values due to some deleted original edges. */ - *val_p = POINTER_FROM_INT(new_eidx); - created_edges++; - - last_valid_ml->e = new_eidx; - } - need_edge_to_first_valid_ml = need_edge_from_last_valid_ml = false; - } - - if (UNLIKELY(c == 0)) { - BLI_assert(created_edges == 0); - continue; - } - if (UNLIKELY(c < 3)) { - STACK_DISCARD(oldl, c); - STACK_DISCARD(mloop, c); - if (created_edges > 0) { - for (j = STACK_SIZE(medge) - created_edges; j < STACK_SIZE(medge); j++) { - BLI_edgehash_remove(ehash, medge[j].v1, medge[j].v2, NULL); - } - STACK_DISCARD(olde, created_edges); - STACK_DISCARD(medge, created_edges); - } - continue; - } - - mp_new = STACK_PUSH_RET_PTR(mpoly); - *mp_new = *mp; - mp_new->totloop = c; - BLI_assert(mp_new->totloop >= 3); - mp_new->loopstart = STACK_SIZE(mloop) - c; - - STACK_PUSH(oldp, i); - } /* End of the loop that tests polys. */ - - if (poly_gset) { - // printf("hash quality %.6f\n", BLI_gset_calc_quality(poly_gset)); - - BLI_gset_free(poly_gset, NULL); - MEM_freeN(poly_keys); - } - - /* Create new cddm. */ - result = BKE_mesh_new_nomain_from_template( - mesh, totvert_final, STACK_SIZE(medge), 0, STACK_SIZE(mloop), STACK_SIZE(mpoly)); - - /* Update edge indices and copy customdata. */ - MEdge *new_med = medge; - for (i = 0; i < result->totedge; i++, new_med++) { - BLI_assert(newv[new_med->v1] != -1); - new_med->v1 = newv[new_med->v1]; - BLI_assert(newv[new_med->v2] != -1); - new_med->v2 = newv[new_med->v2]; - - /* Can happen in case vtargetmap contains some double chains, we do not support that. */ - BLI_assert(new_med->v1 != new_med->v2); - - CustomData_copy_data(&mesh->edata, &result->edata, olde[i], i, 1); - } - - /* Update loop indices and copy customdata. */ - MLoop *new_ml = mloop; - for (i = 0; i < result->totloop; i++, new_ml++) { - /* Edge remapping has already be done in main loop handling part above. */ - BLI_assert(newv[new_ml->v] != -1); - new_ml->v = newv[new_ml->v]; - - CustomData_copy_data(&mesh->ldata, &result->ldata, oldl[i], i, 1); - } - - /* Copy vertex customdata. */ - for (i = 0; i < result->totvert; i++) { - CustomData_copy_data(&mesh->vdata, &result->vdata, oldv[i], i, 1); - } - - /* Copy poly customdata. */ - mp = mpoly; - for (i = 0; i < result->totpoly; i++, mp++) { - CustomData_copy_data(&mesh->pdata, &result->pdata, oldp[i], i, 1); - } - - /* Copy over data. #CustomData_add_layer can do this, need to look it up. */ - if (STACK_SIZE(medge)) { - memcpy(BKE_mesh_edges_for_write(result), medge, sizeof(MEdge) * STACK_SIZE(medge)); - } - if (STACK_SIZE(mloop)) { - memcpy(BKE_mesh_loops_for_write(result), mloop, sizeof(MLoop) * STACK_SIZE(mloop)); - } - if (STACK_SIZE(mpoly)) { - memcpy(BKE_mesh_polys_for_write(result), mpoly, sizeof(MPoly) * STACK_SIZE(mpoly)); - } - - MEM_freeN(medge); - MEM_freeN(mloop); - MEM_freeN(mpoly); - - MEM_freeN(newv); - MEM_freeN(newe); -#ifdef USE_LOOPS - MEM_freeN(newl); -#endif - - MEM_freeN(oldv); - MEM_freeN(olde); - MEM_freeN(oldl); - MEM_freeN(oldp); - - MEM_freeN(vert_tag); - - BLI_edgehash_free(ehash, NULL); - - if (poly_map != NULL) { - MEM_freeN(poly_map); - } - if (poly_map_mem != NULL) { - MEM_freeN(poly_map_mem); - } - - BKE_id_free(NULL, mesh); - - return result; -} diff --git a/source/blender/blenkernel/intern/mesh_merge_customdata.cc b/source/blender/blenkernel/intern/mesh_merge_customdata.cc index c10c9d851b9..6ceedc66082 100644 --- a/source/blender/blenkernel/intern/mesh_merge_customdata.cc +++ b/source/blender/blenkernel/intern/mesh_merge_customdata.cc @@ -116,8 +116,8 @@ void BKE_mesh_merge_customdata_for_apply_modifier(Mesh *me) struct MeshElemMap *vert_to_loop; BKE_mesh_vert_loop_map_create(&vert_to_loop, &vert_map_mem, - BKE_mesh_polys(me), - BKE_mesh_loops(me), + me->polys().data(), + me->loops().data(), me->totvert, me->totpoly, me->totloop); diff --git a/source/blender/blenkernel/intern/mesh_mirror.cc b/source/blender/blenkernel/intern/mesh_mirror.cc index 3c603a5a1f3..768652c031a 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.cc +++ b/source/blender/blenkernel/intern/mesh_mirror.cc @@ -115,11 +115,12 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, Object *ob, const Mesh *mesh, const int axis, - const bool use_correct_order_on_merge) + const bool use_correct_order_on_merge, + int **r_vert_merge_map, + int *r_vert_merge_map_len) { const float tolerance_sq = mmd->tolerance * mmd->tolerance; - const bool do_vtargetmap = (mmd->flag & MOD_MIR_NO_MERGE) == 0; - int tot_vtargetmap = 0; /* total merge vertices */ + const bool do_vtargetmap = (mmd->flag & MOD_MIR_NO_MERGE) == 0 && r_vert_merge_map != nullptr; const bool do_bisect = ((axis == 0 && mmd->flag & MOD_MIR_BISECT_AXIS_X) || (axis == 1 && mmd->flag & MOD_MIR_BISECT_AXIS_Y) || @@ -133,7 +134,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, float plane_co[3], plane_no[3]; int i; int a, totshape; - int *vtargetmap = nullptr, *vtmap_a = nullptr, *vtmap_b = nullptr; + int *vtmap_a = nullptr, *vtmap_b = nullptr; /* mtx is the mirror transformation */ unit_m4(mtx); @@ -202,21 +203,6 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, maxLoops); CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, maxPolys); - /* Subdivision-surface for eg won't have mesh data in the custom-data arrays. - * Now add position/#MEdge/#MPoly layers. */ - if (BKE_mesh_vert_positions(mesh) != nullptr) { - memcpy(BKE_mesh_vert_positions_for_write(result), - BKE_mesh_vert_positions(mesh), - sizeof(float[3]) * mesh->totvert); - } - if (!CustomData_has_layer(&mesh->edata, CD_MEDGE)) { - memcpy(BKE_mesh_edges_for_write(result), BKE_mesh_edges(mesh), sizeof(MEdge) * mesh->totedge); - } - if (!CustomData_has_layer(&mesh->pdata, CD_MPOLY)) { - memcpy(BKE_mesh_loops_for_write(result), BKE_mesh_loops(mesh), sizeof(MLoop) * mesh->totloop); - memcpy(BKE_mesh_polys_for_write(result), BKE_mesh_polys(mesh), sizeof(MPoly) * mesh->totpoly); - } - /* Copy custom-data to new geometry, * copy from itself because this data may have been created in the checks above. */ CustomData_copy_data(&result->vdata, &result->vdata, 0, maxVerts, maxVerts); @@ -226,11 +212,13 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, if (do_vtargetmap) { /* second half is filled with -1 */ - vtargetmap = static_cast( + *r_vert_merge_map = static_cast( MEM_malloc_arrayN(maxVerts, sizeof(int[2]), "MOD_mirror tarmap")); - vtmap_a = vtargetmap; - vtmap_b = vtargetmap + maxVerts; + vtmap_a = *r_vert_merge_map; + vtmap_b = *r_vert_merge_map + maxVerts; + + *r_vert_merge_map_len = 0; } /* mirror vertex coordinates */ @@ -258,7 +246,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, if (UNLIKELY(len_squared_v3v3(positions[vert_index_prev], positions[vert_index]) < tolerance_sq)) { *vtmap_b = i; - tot_vtargetmap++; + (*r_vert_merge_map_len)++; /* average location */ mid_v3_v3v3(positions[vert_index], positions[vert_index_prev], positions[vert_index]); @@ -275,7 +263,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, if (UNLIKELY(len_squared_v3v3(positions[vert_index_prev], positions[vert_index]) < tolerance_sq)) { *vtmap_a = maxVerts + i; - tot_vtargetmap++; + (*r_vert_merge_map_len)++; /* average location */ mid_v3_v3v3(positions[vert_index], positions[vert_index_prev], positions[vert_index]); @@ -305,14 +293,14 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, } /* adjust mirrored edge vertex indices */ - me = BKE_mesh_edges_for_write(result) + maxEdges; + me = result->edges_for_write().data() + maxEdges; for (i = 0; i < maxEdges; i++, me++) { me->v1 += maxVerts; me->v2 += maxVerts; } /* adjust mirrored poly loopstart indices, and reverse loop order (normals) */ - mp = BKE_mesh_polys_for_write(result) + maxPolys; + mp = result->polys_for_write().data() + maxPolys; ml = BKE_mesh_loops_for_write(result); for (i = 0; i < maxPolys; i++, mp++) { MLoop *ml2; @@ -414,12 +402,12 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, BKE_mesh_normals_loop_split(BKE_mesh_vert_positions(result), BKE_mesh_vertex_normals_ensure(result), result->totvert, - BKE_mesh_edges(result), + result->edges().data(), result->totedge, - BKE_mesh_loops(result), + result->loops().data(), loop_normals, totloop, - BKE_mesh_polys(result), + result->polys().data(), BKE_mesh_poly_normals_ensure(result), totpoly, true, @@ -430,16 +418,15 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, clnors); /* mirroring has to account for loops being reversed in polys in second half */ - MPoly *result_polys = BKE_mesh_polys_for_write(result); - mp = result_polys; - for (i = 0; i < maxPolys; i++, mp++) { - MPoly *mpmirror = result_polys + maxPolys + i; + blender::MutableSpan result_polys = result->polys_for_write(); + for (i = 0; i < maxPolys; i++) { + const int mirror_i = maxPolys + i; int j; for (j = mp->loopstart; j < mp->loopstart + mp->totloop; j++) { - int mirrorj = mpmirror->loopstart; + int mirrorj = result_polys[mirror_i].loopstart; if (j > mp->loopstart) { - mirrorj += mpmirror->totloop - (j - mp->loopstart); + mirrorj += result_polys[mirror_i].totloop - (j - mp->loopstart); } copy_v3_v3(loop_normals[mirrorj], loop_normals[j]); mul_m4_v3(mtx_nor, loop_normals[mirrorj]); @@ -461,10 +448,12 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, if (flip_map) { for (i = 0; i < maxVerts; dvert++, i++) { /* merged vertices get both groups, others get flipped */ - if (use_correct_order_on_merge && do_vtargetmap && (vtargetmap[i + maxVerts] != -1)) { + if (use_correct_order_on_merge && do_vtargetmap && + ((*r_vert_merge_map)[i + maxVerts] != -1)) { BKE_defvert_flip_merged(dvert - maxVerts, flip_map, flip_map_len); } - else if (!use_correct_order_on_merge && do_vtargetmap && (vtargetmap[i] != -1)) { + else if (!use_correct_order_on_merge && do_vtargetmap && + ((*r_vert_merge_map)[i] != -1)) { BKE_defvert_flip_merged(dvert, flip_map, flip_map_len); } else { @@ -477,16 +466,6 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, } } - if (do_vtargetmap) { - /* slow - so only call if one or more merge verts are found, - * users may leave this on and not realize there is nothing to merge - campbell */ - if (tot_vtargetmap) { - result = BKE_mesh_merge_verts( - result, vtargetmap, tot_vtargetmap, MESH_MERGE_VERTS_DUMP_IF_MAPPED); - } - MEM_freeN(vtargetmap); - } - if (mesh_bisect != nullptr) { BKE_id_free(nullptr, mesh_bisect); } diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index c83de7a843a..a3e34224a02 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -42,6 +42,7 @@ using blender::BitVector; using blender::float3; using blender::int2; +using blender::MutableBitSpan; using blender::MutableSpan; using blender::short2; using blender::Span; @@ -1238,7 +1239,7 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const Span mloop const Span edge_to_loops, const Span loop_to_poly, const int *e2l_prev, - BitVector<> &skip_loops, + MutableBitSpan skip_loops, const int ml_curr_index, const int ml_prev_index, const int mp_curr_index) diff --git a/source/blender/blenkernel/intern/mesh_remap.cc b/source/blender/blenkernel/intern/mesh_remap.cc index 8c25315ab19..73e869a9ce1 100644 --- a/source/blender/blenkernel/intern/mesh_remap.cc +++ b/source/blender/blenkernel/intern/mesh_remap.cc @@ -371,8 +371,8 @@ void BKE_mesh_remap_item_define_invalid(MeshPairRemap *map, const int index) mesh_remap_item_define(map, index, FLT_MAX, 0, 0, nullptr, nullptr); } -static int mesh_remap_interp_poly_data_get(const MPoly *mp, - const MLoop *mloops, +static int mesh_remap_interp_poly_data_get(const MPoly &poly, + const blender::Span loops, const float (*vcos_src)[3], const float point[3], size_t *buff_size, @@ -387,7 +387,7 @@ static int mesh_remap_interp_poly_data_get(const MPoly *mp, float(*vco)[3]; float ref_dist_sq = FLT_MAX; int *index; - const int sources_num = mp->totloop; + const int sources_num = poly.totloop; int i; if (size_t(sources_num) > *buff_size) { @@ -399,9 +399,9 @@ static int mesh_remap_interp_poly_data_get(const MPoly *mp, } } - for (i = 0, ml = &mloops[mp->loopstart], vco = *vcos, index = *indices; i < sources_num; + for (i = 0, ml = &loops[poly.loopstart], vco = *vcos, index = *indices; i < sources_num; i++, ml++, vco++, index++) { - *index = use_loops ? int(mp->loopstart) + i : int(ml->v); + *index = use_loops ? int(poly.loopstart) + i : int(ml->v); copy_v3_v3(*vco, vcos_src[ml->v]); if (r_closest_index) { /* Find closest vert/loop in this case. */ @@ -515,7 +515,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode, } } else if (ELEM(mode, MREMAP_MODE_VERT_EDGE_NEAREST, MREMAP_MODE_VERT_EDGEINTERP_NEAREST)) { - const MEdge *edges_src = BKE_mesh_edges(me_src); + const blender::Span edges_src = me_src->edges(); float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, nullptr); BKE_bvhtree_from_mesh_get(&treedata, me_src, BVHTREE_FROM_EDGES, 2); @@ -568,8 +568,8 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode, MREMAP_MODE_VERT_POLY_NEAREST, MREMAP_MODE_VERT_POLYINTERP_NEAREST, MREMAP_MODE_VERT_POLYINTERP_VNORPROJ)) { - const MPoly *polys_src = BKE_mesh_polys(me_src); - const MLoop *loops_src = BKE_mesh_loops(me_src); + const blender::Span polys_src = me_src->polys(); + const blender::Span loops_src = me_src->loops(); float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, nullptr); const float(*vert_normals_dst)[3] = BKE_mesh_vertex_normals_ensure(me_dst); @@ -596,8 +596,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode, if (mesh_remap_bvhtree_query_raycast( &treedata, &rayhit, tmp_co, tmp_no, ray_radius, max_dist, &hit_dist)) { const MLoopTri *lt = &treedata.looptri[rayhit.index]; - const MPoly *mp_src = &polys_src[lt->poly]; - const int sources_num = mesh_remap_interp_poly_data_get(mp_src, + const int sources_num = mesh_remap_interp_poly_data_get(polys_src[lt->poly], loops_src, (const float(*)[3])vcos_src, rayhit.co, @@ -631,11 +630,10 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode, if (mesh_remap_bvhtree_query_nearest( &treedata, &nearest, tmp_co, max_dist_sq, &hit_dist)) { const MLoopTri *lt = &treedata.looptri[nearest.index]; - const MPoly *mp = &polys_src[lt->poly]; if (mode == MREMAP_MODE_VERT_POLY_NEAREST) { int index; - mesh_remap_interp_poly_data_get(mp, + mesh_remap_interp_poly_data_get(polys_src[lt->poly], loops_src, (const float(*)[3])vcos_src, nearest.co, @@ -650,7 +648,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode, mesh_remap_item_define(r_map, i, hit_dist, 0, 1, &index, &full_weight); } else if (mode == MREMAP_MODE_VERT_POLYINTERP_NEAREST) { - const int sources_num = mesh_remap_interp_poly_data_get(mp, + const int sources_num = mesh_remap_interp_poly_data_get(polys_src[lt->poly], loops_src, (const float(*)[3])vcos_src, nearest.co, @@ -722,8 +720,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode, if (mode == MREMAP_MODE_EDGE_VERT_NEAREST) { const int num_verts_src = me_src->totvert; - const int num_edges_src = me_src->totedge; - const MEdge *edges_src = BKE_mesh_edges(me_src); + const blender::Span edges_src = me_src->edges(); float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, nullptr); MeshElemMap *vert_to_edge_src_map; @@ -742,9 +739,9 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode, BKE_mesh_vert_edge_map_create(&vert_to_edge_src_map, &vert_to_edge_src_map_mem, - edges_src, + edges_src.data(), num_verts_src, - num_edges_src); + int(edges_src.size())); BKE_bvhtree_from_mesh_get(&treedata, me_src, BVHTREE_FROM_VERTS, 2); nearest.index = -1; @@ -874,9 +871,9 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode, } } else if (mode == MREMAP_MODE_EDGE_POLY_NEAREST) { - const MEdge *edges_src = BKE_mesh_edges(me_src); - const MPoly *polys_src = BKE_mesh_polys(me_src); - const MLoop *loops_src = BKE_mesh_loops(me_src); + const blender::Span edges_src = me_src->edges(); + const blender::Span polys_src = me_src->polys(); + const blender::Span loops_src = me_src->loops(); float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, nullptr); BKE_bvhtree_from_mesh_get(&treedata, me_src, BVHTREE_FROM_LOOPTRI, 2); @@ -1311,12 +1308,9 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, const float(*positions_src)[3] = BKE_mesh_vert_positions(me_src); const int num_verts_src = me_src->totvert; float(*vcos_src)[3] = nullptr; - const MEdge *edges_src = BKE_mesh_edges(me_src); - const int num_edges_src = me_src->totedge; - const MLoop *loops_src = BKE_mesh_loops(me_src); - const int num_loops_src = me_src->totloop; - const MPoly *polys_src = BKE_mesh_polys(me_src); - const int num_polys_src = me_src->totpoly; + const blender::Span edges_src = me_src->edges(); + const blender::Span polys_src = me_src->polys(); + const blender::Span loops_src = me_src->loops(); const MLoopTri *looptri_src = nullptr; int num_looptri_src = 0; @@ -1327,7 +1321,6 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, const MLoop *ml_src; const MLoop *ml_dst; - const MPoly *mp_src; const MPoly *mp_dst; int tindex, pidx_dst, lidx_dst, plidx_dst, pidx_src, lidx_src, plidx_src; @@ -1405,42 +1398,43 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, if (use_from_vert) { BKE_mesh_vert_loop_map_create(&vert_to_loop_map_src, &vert_to_loop_map_src_buff, - polys_src, - loops_src, + polys_src.data(), + loops_src.data(), num_verts_src, - num_polys_src, - num_loops_src); + int(polys_src.size()), + int(loops_src.size())); if (mode & MREMAP_USE_POLY) { BKE_mesh_vert_poly_map_create(&vert_to_poly_map_src, &vert_to_poly_map_src_buff, - polys_src, - loops_src, + polys_src.data(), + loops_src.data(), num_verts_src, - num_polys_src, - num_loops_src); + int(polys_src.size()), + int(loops_src.size())); } } /* Needed for islands (or plain mesh) to AStar graph conversion. */ BKE_mesh_edge_poly_map_create(&edge_to_poly_map_src, &edge_to_poly_map_src_buff, - num_edges_src, - polys_src, - num_polys_src, - loops_src, - num_loops_src); + int(edges_src.size()), + polys_src.data(), + int(polys_src.size()), + loops_src.data(), + int(loops_src.size())); if (use_from_vert) { loop_to_poly_map_src = static_cast( - MEM_mallocN(sizeof(*loop_to_poly_map_src) * size_t(num_loops_src), __func__)); + MEM_mallocN(sizeof(*loop_to_poly_map_src) * size_t(loops_src.size()), __func__)); poly_cents_src = static_cast( - MEM_mallocN(sizeof(*poly_cents_src) * size_t(num_polys_src), __func__)); - for (pidx_src = 0, mp_src = polys_src; pidx_src < num_polys_src; pidx_src++, mp_src++) { - ml_src = &loops_src[mp_src->loopstart]; - for (plidx_src = 0, lidx_src = mp_src->loopstart; plidx_src < mp_src->totloop; + MEM_mallocN(sizeof(*poly_cents_src) * size_t(polys_src.size()), __func__)); + for (pidx_src = 0; pidx_src < polys_src.size(); pidx_src++) { + const MPoly &poly = polys_src[pidx_src]; + ml_src = &loops_src[poly.loopstart]; + for (plidx_src = 0, lidx_src = poly.loopstart; plidx_src < poly.totloop; plidx_src++, lidx_src++) { loop_to_poly_map_src[lidx_src] = pidx_src; } - BKE_mesh_calc_poly_center(mp_src, ml_src, positions_src, poly_cents_src[pidx_src]); + BKE_mesh_calc_poly_center(&poly, ml_src, positions_src, poly_cents_src[pidx_src]); } } @@ -1455,12 +1449,12 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, if (gen_islands_src) { use_islands = gen_islands_src(positions_src, num_verts_src, - edges_src, - num_edges_src, - polys_src, - num_polys_src, - loops_src, - num_loops_src, + edges_src.data(), + int(edges_src.size()), + polys_src.data(), + int(polys_src.size()), + loops_src.data(), + int(loops_src.size()), &island_store); num_trees = use_islands ? island_store.islands_num : 1; @@ -1495,10 +1489,10 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, tindex, positions_src, edge_to_poly_map_src, - num_edges_src, - loops_src, - polys_src, - num_polys_src, + int(edges_src.size()), + loops_src.data(), + polys_src.data(), + int(polys_src.size()), &as_graphdata[tindex]); } } @@ -1513,9 +1507,8 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, int num_verts_active = 0; verts_active.fill(false); for (i = 0; i < isld->count; i++) { - mp_src = &polys_src[isld->indices[i]]; - for (lidx_src = mp_src->loopstart; lidx_src < mp_src->loopstart + mp_src->totloop; - lidx_src++) { + const MPoly &poly = polys_src[isld->indices[i]]; + for (lidx_src = poly.loopstart; lidx_src < poly.loopstart + poly.totloop; lidx_src++) { const uint vidx_src = loops_src[lidx_src].v; if (!verts_active[vidx_src]) { verts_active[vidx_src].set(); @@ -1549,15 +1542,15 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, int num_looptri_active = 0; looptri_active.fill(false); for (i = 0; i < num_looptri_src; i++) { - mp_src = &polys_src[looptri_src[i].poly]; - if (island_store.items_to_islands[mp_src->loopstart] == tindex) { + const MPoly &poly = polys_src[looptri_src[i].poly]; + if (island_store.items_to_islands[poly.loopstart] == tindex) { looptri_active[i].set(); num_looptri_active++; } } bvhtree_from_mesh_looptri_ex(&treedata[tindex], positions_src, - loops_src, + loops_src.data(), looptri_src, num_looptri_src, looptri_active, @@ -1670,8 +1663,8 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, float *pcent_src; float sqdist; - mp_src = &polys_src[pidx_src]; - ml_src = &loops_src[mp_src->loopstart]; + const MPoly &poly = polys_src[pidx_src]; + ml_src = &loops_src[poly.loopstart]; if (!pcent_dst_valid) { BKE_mesh_calc_poly_center( @@ -1696,11 +1689,11 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, else if (mode == MREMAP_MODE_LOOP_NEAREST_POLYNOR) { /* Our best_index_src is a poly one for now! * Have to find its loop matching our closest vertex. */ - mp_src = &polys_src[best_index_src]; - ml_src = &loops_src[mp_src->loopstart]; - for (plidx_src = 0; plidx_src < mp_src->totloop; plidx_src++, ml_src++) { + const MPoly &poly = polys_src[best_index_src]; + ml_src = &loops_src[poly.loopstart]; + for (plidx_src = 0; plidx_src < poly.totloop; plidx_src++, ml_src++) { if (int(ml_src->v) == nearest.index) { - best_index_src = plidx_src + mp_src->loopstart; + best_index_src = plidx_src + poly.loopstart; break; } } @@ -1915,13 +1908,13 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, pidx_src = (use_islands ? best_island->indices[last_valid_pidx_isld_src] : last_valid_pidx_isld_src); - mp_src = &polys_src[pidx_src]; - ml_src = &loops_src[mp_src->loopstart]; - for (j = 0; j < mp_src->totloop; j++, ml_src++) { + const MPoly &poly = polys_src[pidx_src]; + ml_src = &loops_src[poly.loopstart]; + for (j = 0; j < poly.totloop; j++, ml_src++) { const float dist_sq = len_squared_v3v3(positions_src[ml_src->v], tmp_co); if (dist_sq < best_dist_sq) { best_dist_sq = dist_sq; - lidx_src = mp_src->loopstart + j; + lidx_src = poly.loopstart + j; } } } @@ -1951,7 +1944,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, float *hit_co = isld_res->hit_point; int best_loop_index_src; - mp_src = &polys_src[pidx_src]; + const MPoly &poly = polys_src[pidx_src]; /* If prev and curr poly are the same, no need to do anything more!!! */ if (!ELEM(pidx_src_prev, -1, pidx_src) && isld_steps_src) { int pidx_isld_src, pidx_isld_src_prev; @@ -2008,14 +2001,13 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, pidx_src = (use_islands ? best_island->indices[last_valid_pidx_isld_src] : last_valid_pidx_isld_src); - mp_src = &polys_src[pidx_src]; /* Create that one on demand. */ if (poly_to_looptri_map_src == nullptr) { BKE_mesh_origindex_map_create_looptri(&poly_to_looptri_map_src, &poly_to_looptri_map_src_buff, - polys_src, - num_polys_src, + polys_src.data(), + int(polys_src.size()), looptri_src, num_looptri_src); } @@ -2042,7 +2034,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, } if (mode == MREMAP_MODE_LOOP_POLY_NEAREST) { - mesh_remap_interp_poly_data_get(mp_src, + mesh_remap_interp_poly_data_get(poly, loops_src, (const float(*)[3])vcos_src, hit_co, @@ -2064,7 +2056,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, } else { const int sources_num = mesh_remap_interp_poly_data_get( - mp_src, + poly, loops_src, (const float(*)[3])vcos_src, hit_co, diff --git a/source/blender/blenkernel/intern/mesh_tangent.cc b/source/blender/blenkernel/intern/mesh_tangent.cc index b0bc16b2732..d40c9a458db 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.cc +++ b/source/blender/blenkernel/intern/mesh_tangent.cc @@ -146,12 +146,12 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh, BKE_mesh_calc_loop_tangent_single_ex(BKE_mesh_vert_positions(mesh), mesh->totvert, - BKE_mesh_loops(mesh), + mesh->loops().data(), r_looptangents, loop_normals, reinterpret_cast(uv_map.data()), mesh->totloop, - BKE_mesh_polys(mesh), + mesh->polys().data(), mesh->totpoly, reports); } @@ -577,9 +577,9 @@ void BKE_mesh_calc_loop_tangents(Mesh *me_eval, short tangent_mask = 0; BKE_mesh_calc_loop_tangent_ex( BKE_mesh_vert_positions(me_eval), - BKE_mesh_polys(me_eval), + me_eval->polys().data(), uint(me_eval->totpoly), - BKE_mesh_loops(me_eval), + me_eval->loops().data(), BKE_mesh_runtime_looptri_ensure(me_eval), uint(BKE_mesh_runtime_looptri_len(me_eval)), &me_eval->ldata, diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc index 555ae98e2d8..a7e5d999cf6 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.cc +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -40,7 +40,7 @@ #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_subdiv.h" -#include "BKE_subdiv_mesh.h" +#include "BKE_subdiv_mesh.hh" #include "BKE_subdiv_modifier.h" #include "DEG_depsgraph.h" diff --git a/source/blender/blenkernel/intern/multires.cc b/source/blender/blenkernel/intern/multires.cc index aaa35e02747..49084ca12cb 100644 --- a/source/blender/blenkernel/intern/multires.cc +++ b/source/blender/blenkernel/intern/multires.cc @@ -183,15 +183,15 @@ static BLI_bitmap *multires_mdisps_downsample_hidden(const BLI_bitmap *old_hidde static void multires_output_hidden_to_ccgdm(CCGDerivedMesh *ccgdm, Mesh *me, int level) { - const MPoly *polys = BKE_mesh_polys(me); + const blender::Span polys = me->polys(); const MDisps *mdisps = static_cast(CustomData_get_layer(&me->ldata, CD_MDISPS)); BLI_bitmap **grid_hidden = ccgdm->gridHidden; int *gridOffset; - int i, j; + int j; gridOffset = ccgdm->dm.getGridOffset(&ccgdm->dm); - for (i = 0; i < me->totpoly; i++) { + for (const int i : polys.index_range()) { for (j = 0; j < polys[i].totloop; j++) { int g = gridOffset[i] + j; const MDisps *md = &mdisps[g]; @@ -487,12 +487,12 @@ void multires_force_external_reload(Object *object) static int get_levels_from_disps(Object *ob) { Mesh *me = static_cast(ob->data); - const MPoly *polys = BKE_mesh_polys(me); - int i, j, totlvl = 0; + const blender::Span polys = me->polys(); + int j, totlvl = 0; const MDisps *mdisp = static_cast(CustomData_get_layer(&me->ldata, CD_MDISPS)); - for (i = 0; i < me->totpoly; i++) { + for (const int i : polys.index_range()) { const MDisps *md = mdisp + polys[i].loopstart; for (j = 0; j < polys[i].totloop; j++, md++) { @@ -657,7 +657,7 @@ static void multires_grid_paint_mask_downsample(GridPaintMask *gpm, int level) static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl) { Mesh *me = (Mesh *)ob->data; - const MPoly *polys = BKE_mesh_polys(me); + const blender::Span polys = me->polys(); int levels = mmd->totlvl - lvl; MDisps *mdisps; GridPaintMask *gpm; @@ -676,9 +676,9 @@ static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl) // MLoop *ml = me->mloop; /*UNUSED*/ int nsize = multires_side_tot[lvl]; int hsize = multires_side_tot[mmd->totlvl]; - int i, j; + int j; - for (i = 0; i < me->totpoly; i++) { + for (const int i : polys.index_range()) { for (j = 0; j < polys[i].totloop; j++) { int g = polys[i].loopstart + j; MDisps *mdisp = &mdisps[g]; @@ -968,7 +968,7 @@ static void multiresModifier_disp_run( CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; CCGElem **gridData, **subGridData; CCGKey key; - const MPoly *mpoly = BKE_mesh_polys(me); + const MPoly *mpoly = me->polys().data(); MDisps *mdisps = static_cast( CustomData_get_layer_for_write(&me->ldata, CD_MDISPS, me->totloop)); GridPaintMask *grid_paint_mask = nullptr; diff --git a/source/blender/blenkernel/intern/multires_reshape.hh b/source/blender/blenkernel/intern/multires_reshape.hh index 8e112f18d7b..d709afca86f 100644 --- a/source/blender/blenkernel/intern/multires_reshape.hh +++ b/source/blender/blenkernel/intern/multires_reshape.hh @@ -7,6 +7,7 @@ #pragma once +#include "BLI_span.hh" #include "BLI_sys_types.h" #include "BKE_multires.h" @@ -34,9 +35,9 @@ struct MultiresReshapeContext { * NOTE: Does NOT include any leading modifiers in it. */ Mesh *base_mesh; const float (*base_positions)[3]; - const MEdge *base_edges; - const MPoly *base_polys; - const MLoop *base_loops; + blender::Span base_edges; + blender::Span base_polys; + blender::Span base_loops; /* Subdivision surface created for multires modifier. * diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.cc b/source/blender/blenkernel/intern/multires_reshape_apply_base.cc index f2c2df9d7ce..50083ada4a1 100644 --- a/source/blender/blenkernel/intern/multires_reshape_apply_base.cc +++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.cc @@ -34,9 +34,9 @@ void multires_reshape_apply_base_update_mesh_coords(MultiresReshapeContext *resh /* Update the context in case the vertices were duplicated. */ reshape_context->base_positions = base_positions; - const MLoop *mloop = reshape_context->base_loops; - for (int loop_index = 0; loop_index < base_mesh->totloop; ++loop_index) { - const MLoop *loop = &mloop[loop_index]; + const blender::Span loops = reshape_context->base_loops; + for (const int loop_index : loops.index_range()) { + const MLoop *loop = &loops[loop_index]; GridCoord grid_coord; grid_coord.grid_index = loop_index; @@ -75,8 +75,8 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape int *pmap_mem; BKE_mesh_vert_poly_map_create(&pmap, &pmap_mem, - reshape_context->base_polys, - reshape_context->base_loops, + reshape_context->base_polys.data(), + reshape_context->base_loops.data(), base_mesh->totvert, base_mesh->totpoly, base_mesh->totloop); diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.cc b/source/blender/blenkernel/intern/multires_reshape_smooth.cc index 19cad4e8e71..a245560d6e7 100644 --- a/source/blender/blenkernel/intern/multires_reshape_smooth.cc +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.cc @@ -22,8 +22,8 @@ #include "BKE_multires.h" #include "BKE_subdiv.h" #include "BKE_subdiv_eval.h" -#include "BKE_subdiv_foreach.h" -#include "BKE_subdiv_mesh.h" +#include "BKE_subdiv_foreach.hh" +#include "BKE_subdiv_mesh.hh" #include "opensubdiv_converter_capi.h" #include "opensubdiv_evaluator_capi.h" @@ -849,17 +849,17 @@ static void geometry_init_loose_information(MultiresReshapeSmoothContext *reshap { const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; const Mesh *base_mesh = reshape_context->base_mesh; - const MPoly *base_mpoly = reshape_context->base_polys; - const MLoop *base_mloop = reshape_context->base_loops; + const blender::Span base_polys = reshape_context->base_polys; + const blender::Span base_loops = reshape_context->base_loops; reshape_smooth_context->non_loose_base_edge_map = BLI_BITMAP_NEW(base_mesh->totedge, "non_loose_base_edge_map"); int num_used_edges = 0; - for (int poly_index = 0; poly_index < base_mesh->totpoly; ++poly_index) { - const MPoly *base_poly = &base_mpoly[poly_index]; + for (const int poly_index : base_polys.index_range()) { + const MPoly *base_poly = &base_polys[poly_index]; for (int corner = 0; corner < base_poly->totloop; corner++) { - const MLoop *loop = &base_mloop[base_poly->loopstart + corner]; + const MLoop *loop = &base_loops[base_poly->loopstart + corner]; if (!BLI_BITMAP_TEST_BOOL(reshape_smooth_context->non_loose_base_edge_map, loop->e)) { BLI_BITMAP_ENABLE(reshape_smooth_context->non_loose_base_edge_map, loop->e); diff --git a/source/blender/blenkernel/intern/multires_reshape_subdivide.cc b/source/blender/blenkernel/intern/multires_reshape_subdivide.cc index 724e07a7d44..4bef8b6ee0f 100644 --- a/source/blender/blenkernel/intern/multires_reshape_subdivide.cc +++ b/source/blender/blenkernel/intern/multires_reshape_subdivide.cc @@ -29,13 +29,12 @@ static void multires_subdivide_create_object_space_linear_grids(Mesh *mesh) { const float(*positions)[3] = BKE_mesh_vert_positions(mesh); - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); MDisps *mdisps = static_cast( CustomData_get_layer_for_write(&mesh->ldata, CD_MDISPS, mesh->totloop)); - const int totpoly = mesh->totpoly; - for (int p = 0; p < totpoly; p++) { + for (const int p : polys.index_range()) { const MPoly *poly = &polys[p]; float poly_center[3]; BKE_mesh_calc_poly_center(poly, &loops[poly->loopstart], positions, poly_center); diff --git a/source/blender/blenkernel/intern/multires_reshape_util.cc b/source/blender/blenkernel/intern/multires_reshape_util.cc index 93d1662de14..f5f6175c21f 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.cc +++ b/source/blender/blenkernel/intern/multires_reshape_util.cc @@ -24,8 +24,8 @@ #include "BKE_subdiv.h" #include "BKE_subdiv_ccg.h" #include "BKE_subdiv_eval.h" -#include "BKE_subdiv_foreach.h" -#include "BKE_subdiv_mesh.h" +#include "BKE_subdiv_foreach.hh" +#include "BKE_subdiv_mesh.hh" #include "DEG_depsgraph_query.h" @@ -61,13 +61,13 @@ Subdiv *multires_reshape_create_subdiv(Depsgraph *depsgraph, static void context_zero(MultiresReshapeContext *reshape_context) { - memset(reshape_context, 0, sizeof(*reshape_context)); + *reshape_context = {}; } static void context_init_lookup(MultiresReshapeContext *reshape_context) { const Mesh *base_mesh = reshape_context->base_mesh; - const MPoly *mpoly = reshape_context->base_polys; + const blender::Span polys = reshape_context->base_polys; const int num_faces = base_mesh->totpoly; reshape_context->face_start_grid_index = static_cast( @@ -75,7 +75,7 @@ static void context_init_lookup(MultiresReshapeContext *reshape_context) int num_grids = 0; int num_ptex_faces = 0; for (int face_index = 0; face_index < num_faces; ++face_index) { - const int num_corners = mpoly[face_index].totloop; + const int num_corners = polys[face_index].totloop; reshape_context->face_start_grid_index[face_index] = num_grids; num_grids += num_corners; num_ptex_faces += (num_corners == 4) ? 1 : num_corners; @@ -86,7 +86,7 @@ static void context_init_lookup(MultiresReshapeContext *reshape_context) reshape_context->ptex_start_grid_index = static_cast( MEM_malloc_arrayN(num_ptex_faces, sizeof(int), "ptex_start_grid_index")); for (int face_index = 0, grid_index = 0, ptex_index = 0; face_index < num_faces; ++face_index) { - const int num_corners = mpoly[face_index].totloop; + const int num_corners = polys[face_index].totloop; const int num_face_ptex_faces = (num_corners == 4) ? 1 : num_corners; for (int i = 0; i < num_face_ptex_faces; ++i) { reshape_context->ptex_start_grid_index[ptex_index + i] = grid_index + i; @@ -156,9 +156,9 @@ bool multires_reshape_context_create_from_base_mesh(MultiresReshapeContext *resh reshape_context->base_mesh = base_mesh; reshape_context->base_positions = BKE_mesh_vert_positions(base_mesh); - reshape_context->base_edges = BKE_mesh_edges(base_mesh); - reshape_context->base_polys = BKE_mesh_polys(base_mesh); - reshape_context->base_loops = BKE_mesh_loops(base_mesh); + reshape_context->base_edges = base_mesh->edges(); + reshape_context->base_polys = base_mesh->polys(); + reshape_context->base_loops = base_mesh->loops(); reshape_context->subdiv = multires_reshape_create_subdiv(nullptr, object, mmd); reshape_context->need_free_subdiv = true; @@ -193,9 +193,9 @@ bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape reshape_context->base_mesh = base_mesh; reshape_context->base_positions = BKE_mesh_vert_positions(base_mesh); - reshape_context->base_edges = BKE_mesh_edges(base_mesh); - reshape_context->base_polys = BKE_mesh_polys(base_mesh); - reshape_context->base_loops = BKE_mesh_loops(base_mesh); + reshape_context->base_edges = base_mesh->edges(); + reshape_context->base_polys = base_mesh->polys(); + reshape_context->base_loops = base_mesh->loops(); reshape_context->subdiv = multires_reshape_create_subdiv(depsgraph, object, mmd); reshape_context->need_free_subdiv = true; @@ -227,9 +227,9 @@ bool multires_reshape_context_create_from_ccg(MultiresReshapeContext *reshape_co reshape_context->base_mesh = base_mesh; reshape_context->base_positions = BKE_mesh_vert_positions(base_mesh); - reshape_context->base_edges = BKE_mesh_edges(base_mesh); - reshape_context->base_polys = BKE_mesh_polys(base_mesh); - reshape_context->base_loops = BKE_mesh_loops(base_mesh); + reshape_context->base_edges = base_mesh->edges(); + reshape_context->base_polys = base_mesh->polys(); + reshape_context->base_loops = base_mesh->loops(); reshape_context->subdiv = subdiv_ccg->subdiv; reshape_context->need_free_subdiv = false; @@ -274,9 +274,9 @@ bool multires_reshape_context_create_from_subdiv(MultiresReshapeContext *reshape reshape_context->mmd = mmd; reshape_context->base_mesh = base_mesh; reshape_context->base_positions = BKE_mesh_vert_positions(base_mesh); - reshape_context->base_edges = BKE_mesh_edges(base_mesh); - reshape_context->base_polys = BKE_mesh_polys(base_mesh); - reshape_context->base_loops = BKE_mesh_loops(base_mesh); + reshape_context->base_edges = base_mesh->edges(); + reshape_context->base_polys = base_mesh->polys(); + reshape_context->base_loops = base_mesh->loops(); reshape_context->cd_vertex_crease = (const float *)CustomData_get_layer(&base_mesh->edata, CD_CREASE); @@ -665,11 +665,11 @@ static void foreach_grid_face_coordinate_task(void *__restrict userdata_v, const MultiresReshapeContext *reshape_context = data->reshape_context; - const MPoly *mpoly = reshape_context->base_polys; + const blender::Span polys = reshape_context->base_polys; const int grid_size = data->grid_size; const float grid_size_1_inv = 1.0f / (float(grid_size) - 1.0f); - const int num_corners = mpoly[face_index].totloop; + const int num_corners = polys[face_index].totloop; int grid_index = reshape_context->face_start_grid_index[face_index]; for (int corner = 0; corner < num_corners; ++corner, ++grid_index) { for (int y = 0; y < grid_size; ++y) { diff --git a/source/blender/blenkernel/intern/multires_reshape_vertcos.cc b/source/blender/blenkernel/intern/multires_reshape_vertcos.cc index 12c67ad104e..711222bdac5 100644 --- a/source/blender/blenkernel/intern/multires_reshape_vertcos.cc +++ b/source/blender/blenkernel/intern/multires_reshape_vertcos.cc @@ -12,8 +12,8 @@ #include "BLI_math_vector.h" -#include "BKE_subdiv_foreach.h" -#include "BKE_subdiv_mesh.h" +#include "BKE_subdiv_foreach.hh" +#include "BKE_subdiv_mesh.hh" struct MultiresReshapeAssignVertcosContext { const MultiresReshapeContext *reshape_context; diff --git a/source/blender/blenkernel/intern/multires_subdiv.cc b/source/blender/blenkernel/intern/multires_subdiv.cc index cb133e93f68..704d3fde1f1 100644 --- a/source/blender/blenkernel/intern/multires_subdiv.cc +++ b/source/blender/blenkernel/intern/multires_subdiv.cc @@ -16,7 +16,7 @@ #include "BKE_modifier.h" #include "BKE_multires.h" #include "BKE_subdiv.h" -#include "BKE_subdiv_mesh.h" +#include "BKE_subdiv_mesh.hh" void BKE_multires_subdiv_settings_init(SubdivSettings *settings, const MultiresModifierData *mmd) { diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.cc b/source/blender/blenkernel/intern/multires_unsubdivide.cc index d6c2e8fd80e..cfcfc25b72f 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.cc +++ b/source/blender/blenkernel/intern/multires_unsubdivide.cc @@ -642,8 +642,8 @@ static void store_grid_data(MultiresUnsubdivideContext *context, int grid_y) { Mesh *original_mesh = context->original_mesh; - const MPoly *polys = BKE_mesh_polys(original_mesh); - const MLoop *loops = BKE_mesh_loops(original_mesh); + const blender::Span polys = original_mesh->polys(); + const blender::Span loops = original_mesh->loops(); const MPoly *poly = &polys[BM_elem_index_get(f)]; const int corner_vertex_index = BM_elem_index_get(v); @@ -921,7 +921,7 @@ static void multires_unsubdivide_prepare_original_bmesh_for_extract( MultiresUnsubdivideContext *context) { Mesh *original_mesh = context->original_mesh; - const MPoly *original_polys = BKE_mesh_polys(original_mesh); + const blender::Span original_polys = original_mesh->polys(); Mesh *base_mesh = context->base_mesh; @@ -966,8 +966,11 @@ static void multires_unsubdivide_prepare_original_bmesh_for_extract( * Checks the orientation of the loops to flip the x and y axis when extracting the grid if * necessary. */ -static bool multires_unsubdivide_flip_grid_x_axis( - const MPoly *polys, const MLoop *loops, int poly, int loop, int v_x) +static bool multires_unsubdivide_flip_grid_x_axis(const blender::Span polys, + const blender::Span loops, + int poly, + int loop, + int v_x) { const MPoly *p = &polys[poly]; @@ -1041,8 +1044,8 @@ static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *conte const int base_l_offset = CustomData_get_n_offset( &bm_base_mesh->ldata, CD_PROP_INT32, base_l_layer_index); - const MPoly *polys = BKE_mesh_polys(base_mesh); - const MLoop *loops = BKE_mesh_loops(base_mesh); + const blender::Span polys = base_mesh->polys(); + const blender::Span loops = base_mesh->loops(); /* Main loop for extracting the grids. Iterates over the base mesh vertices. */ BM_ITER_MESH (v, &iter, bm_base_mesh, BM_VERTS_OF_MESH) { diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 29d4cc802fe..2dbab08ef20 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -91,7 +91,7 @@ void BKE_nlastrip_free(NlaStrip *strip, const bool do_id_user) MEM_freeN(strip); } -void BKE_nlatrack_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user) +void BKE_nlatrack_free(NlaTrack *nlt, const bool do_id_user) { NlaStrip *strip, *stripn; @@ -107,12 +107,7 @@ void BKE_nlatrack_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user) } /* free NLA track itself now */ - if (tracks) { - BLI_freelinkN(tracks, nlt); - } - else { - MEM_freeN(nlt); - } + MEM_freeN(nlt); } void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user) @@ -127,7 +122,7 @@ void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user) /* free tracks one by one */ for (nlt = tracks->first; nlt; nlt = nltn) { nltn = nlt->next; - BKE_nlatrack_free(tracks, nlt, do_id_user); + BKE_nlatrack_remove_and_free(tracks, nlt, do_id_user); } /* clear the list's pointers to be safe */ @@ -514,6 +509,20 @@ void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data) } } +/* Removing ------------------------------------------ */ + +void BKE_nlatrack_remove(ListBase *tracks, struct NlaTrack *nlt) +{ + BLI_assert(tracks); + BLI_remlink(tracks, nlt); +} + +void BKE_nlatrack_remove_and_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user) +{ + BKE_nlatrack_remove(tracks, nlt); + BKE_nlatrack_free(nlt, do_id_user); +} + /* *************************************************** */ /* NLA Evaluation <-> Editing Stuff */ diff --git a/source/blender/blenkernel/intern/nla_test.cc b/source/blender/blenkernel/intern/nla_test.cc index 414391a208f..0520a3b270e 100644 --- a/source/blender/blenkernel/intern/nla_test.cc +++ b/source/blender/blenkernel/intern/nla_test.cc @@ -77,18 +77,43 @@ TEST(nla_track, BKE_nlatrack_remove_strip) strip2.start = 11; strip2.end = 20; - // Add NLA strips to the NLATrack. + /* Add NLA strips to the NLATrack. */ BKE_nlastrips_add_strip(&strips, &strip1); BKE_nlastrips_add_strip(&strips, &strip2); track.strips = strips; - // ensure we have 2 strips in the track. + /* Ensure we have 2 strips in the track. */ EXPECT_EQ(2, BLI_listbase_count(&track.strips)); BKE_nlatrack_remove_strip(&track, &strip2); EXPECT_EQ(1, BLI_listbase_count(&track.strips)); - // ensure the correct strip was removed. + /* Ensure the correct strip was removed. */ EXPECT_EQ(-1, BLI_findindex(&track.strips, &strip2)); } +TEST(nla_track, BKE_nlatrack_remove_and_free) +{ + AnimData adt{}; + NlaTrack *track1; + NlaTrack *track2; + + /* Add NLA tracks to the Animation Data. */ + track1 = BKE_nlatrack_add(&adt, NULL, false); + track2 = BKE_nlatrack_add(&adt, track1, false); + + /* Ensure we have 2 tracks in the track. */ + EXPECT_EQ(2, BLI_listbase_count(&adt.nla_tracks)); + + BKE_nlatrack_remove_and_free(&adt.nla_tracks, track2, false); + EXPECT_EQ(1, BLI_listbase_count(&adt.nla_tracks)); + + /* Ensure the correct track was removed. */ + EXPECT_EQ(-1, BLI_findindex(&adt.nla_tracks, track2)); + + /* Free the rest of the tracks, and ensure they are removed. */ + BKE_nlatrack_remove_and_free(&adt.nla_tracks, track1, false); + EXPECT_EQ(0, BLI_listbase_count(&adt.nla_tracks)); + EXPECT_EQ(-1, BLI_findindex(&adt.nla_tracks, track1)); +} + } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index c8d93c3039d..5c5c670deeb 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1696,16 +1696,16 @@ static void sculpt_update_object( /* These are assigned to the base mesh in Multires. This is needed because Face Sets operators * and tools use the Face Sets data from the base mesh when Multires is active. */ ss->vert_positions = BKE_mesh_vert_positions_for_write(me); - ss->mpoly = BKE_mesh_polys(me); - ss->mloop = BKE_mesh_loops(me); + ss->mpoly = me->polys().data(); + ss->mloop = me->loops().data(); } else { ss->totvert = me->totvert; ss->totpoly = me->totpoly; ss->totfaces = me->totpoly; ss->vert_positions = BKE_mesh_vert_positions_for_write(me); - ss->mpoly = BKE_mesh_polys(me); - ss->mloop = BKE_mesh_loops(me); + ss->mpoly = me->polys().data(); + ss->mloop = me->loops().data(); ss->multires.active = false; ss->multires.modifier = nullptr; ss->multires.level = 0; @@ -1765,8 +1765,8 @@ static void sculpt_update_object( if (need_pmap && ob->type == OB_MESH && !ss->pmap) { BKE_mesh_vert_poly_map_create(&ss->pmap, &ss->pmap_mem, - BKE_mesh_polys(me), - BKE_mesh_loops(me), + me->polys().data(), + me->loops().data(), me->totvert, me->totpoly, me->totloop); diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 7d4769c6e25..ecae54dba94 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -924,10 +924,9 @@ void BKE_pbvh_build_grids(PBVH *pbvh, /* Find maximum number of grids per face. */ int max_grids = 1; - const MPoly *mpoly = BKE_mesh_polys(me); - - for (int i = 0; i < me->totpoly; i++) { - max_grids = max_ii(max_grids, mpoly[i].totloop); + const blender::Span polys = me->polys(); + for (const int i : polys.index_range()) { + max_grids = max_ii(max_grids, polys[i].totloop); } /* Ensure leaf limit is at least 4 so there's room @@ -941,8 +940,8 @@ void BKE_pbvh_build_grids(PBVH *pbvh, pbvh->ldata = &me->ldata; pbvh->pdata = &me->pdata; - pbvh->mpoly = BKE_mesh_polys(me); - pbvh->mloop = BKE_mesh_loops(me); + pbvh->mpoly = me->polys().data(); + pbvh->mloop = me->loops().data(); /* We also need the base mesh for PBVH draw. */ pbvh->mesh = me; @@ -3846,18 +3845,18 @@ void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *mesh) break; } case PBVH_GRIDS: { - const MPoly *mp = BKE_mesh_polys(mesh); + const blender::Span polys = mesh->polys(); CCGKey key = pbvh->gridkey; bool *hide_poly = static_cast(CustomData_get_layer_named_for_write( &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly)); bool delete_hide_poly = true; - for (int face_index = 0; face_index < mesh->totpoly; face_index++, mp++) { + for (const int face_index : polys.index_range()) { bool hidden = false; - for (int loop_index = 0; !hidden && loop_index < mp->totloop; loop_index++) { - int grid_index = mp->loopstart + loop_index; + for (int loop_index = 0; !hidden && loop_index < polys[face_index].totloop; loop_index++) { + int grid_index = polys[face_index].loopstart + loop_index; if (pbvh->grid_hidden[grid_index] && BLI_BITMAP_TEST(pbvh->grid_hidden[grid_index], key.grid_area - 1)) { diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 95ef6b7b925..0f3eccb5bd0 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -956,7 +956,9 @@ ScrArea *BKE_screen_area_map_find_area_xy(const ScrAreaMap *areamap, const int xy[2]) { LISTBASE_FOREACH (ScrArea *, area, &areamap->areabase) { - if (BLI_rcti_isect_pt_v(&area->totrct, xy)) { + /* Test area's outer screen verts, not inner `area->totrct`. */ + if (xy[0] >= area->v1->vec.x && xy[0] <= area->v4->vec.x && xy[1] >= area->v1->vec.y && + xy[1] <= area->v2->vec.y) { if (ELEM(spacetype, SPACE_TYPE_ANY, area->spacetype)) { return area; } diff --git a/source/blender/blenkernel/intern/shrinkwrap.cc b/source/blender/blenkernel/intern/shrinkwrap.cc index 1a0b33ca2b1..0756d04ffc4 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.cc +++ b/source/blender/blenkernel/intern/shrinkwrap.cc @@ -114,7 +114,7 @@ bool BKE_shrinkwrap_init_tree( } data->mesh = mesh; - data->polys = BKE_mesh_polys(mesh); + data->polys = mesh->polys().data(); data->vert_normals = BKE_mesh_vertex_normals_ensure(mesh); if (shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) { @@ -189,15 +189,15 @@ static void merge_vert_dir(ShrinkwrapBoundaryVertData *vdata, static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh) { const float(*positions)[3] = BKE_mesh_vert_positions(mesh); - const MEdge *medge = BKE_mesh_edges(mesh); - const MLoop *mloop = BKE_mesh_loops(mesh); + const blender::Span edges = mesh->edges(); + const blender::Span loops = mesh->loops(); /* Count faces per edge (up to 2). */ char *edge_mode = static_cast( MEM_calloc_arrayN(size_t(mesh->totedge), sizeof(char), __func__)); for (int i = 0; i < mesh->totloop; i++) { - uint eidx = mloop[i].e; + uint eidx = loops[i].e; if (edge_mode[eidx] < 2) { edge_mode[eidx]++; @@ -238,11 +238,11 @@ static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh) "ShrinkwrapBoundaryData::looptri_is_boundary"); for (int i = 0; i < totlooptri; i++) { - int edges[3]; - BKE_mesh_looptri_get_real_edges(medge, mloop, &mlooptri[i], edges); + int real_edges[3]; + BKE_mesh_looptri_get_real_edges(edges.data(), loops.data(), &mlooptri[i], real_edges); for (int j = 0; j < 3; j++) { - if (edges[j] >= 0 && edge_mode[edges[j]]) { + if (real_edges[j] >= 0 && edge_mode[real_edges[j]]) { BLI_BITMAP_ENABLE(looptri_has_boundary, i); break; } @@ -257,7 +257,7 @@ static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh) for (int i = 0; i < mesh->totedge; i++) { if (edge_mode[i]) { - const MEdge *edge = &medge[i]; + const MEdge *edge = &edges[i]; vert_boundary_id[edge->v1] = 1; vert_boundary_id[edge->v2] = 1; @@ -282,7 +282,7 @@ static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(Mesh *mesh) for (int i = 0; i < mesh->totedge; i++) { if (edge_mode[i]) { - const MEdge *edge = &medge[i]; + const MEdge *edge = &edges[i]; float dir[3]; sub_v3_v3v3(dir, positions[edge->v2], positions[edge->v1]); diff --git a/source/blender/blenkernel/intern/subdiv_ccg_mask.cc b/source/blender/blenkernel/intern/subdiv_ccg_mask.cc index 538c4c5eaf4..b76b63c3f5d 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg_mask.cc +++ b/source/blender/blenkernel/intern/subdiv_ccg_mask.cc @@ -29,7 +29,7 @@ struct PolyCornerIndex { struct GridPaintMaskData { // int grid_size; - const MPoly *mpoly; + blender::Span polys; const GridPaintMask *grid_paint_mask; /* Indexed by ptex face index, contains polygon/corner which corresponds * to it. @@ -50,7 +50,7 @@ static int mask_get_grid_and_coord(SubdivCCGMaskEvaluator *mask_evaluator, { GridPaintMaskData *data = static_cast(mask_evaluator->user_data); const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index]; - const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + const MPoly *poly = &data->polys[poly_corner->poly_index]; const int start_grid_index = poly->loopstart + poly_corner->corner; int corner = 0; if (poly->totloop == 4) { @@ -103,9 +103,9 @@ static void free_mask_data(SubdivCCGMaskEvaluator *mask_evaluator) static int count_num_ptex_faces(const Mesh *mesh) { int num_ptex_faces = 0; - const MPoly *mpoly = BKE_mesh_polys(mesh); + const blender::Span polys = mesh->polys(); for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) { - const MPoly *poly = &mpoly[poly_index]; + const MPoly *poly = &polys[poly_index]; num_ptex_faces += (poly->totloop == 4) ? 1 : poly->totloop; } return num_ptex_faces; @@ -114,7 +114,7 @@ static int count_num_ptex_faces(const Mesh *mesh) static void mask_data_init_mapping(SubdivCCGMaskEvaluator *mask_evaluator, const Mesh *mesh) { GridPaintMaskData *data = static_cast(mask_evaluator->user_data); - const MPoly *mpoly = BKE_mesh_polys(mesh); + const blender::Span polys = mesh->polys(); const int num_ptex_faces = count_num_ptex_faces(mesh); /* Allocate memory. */ data->ptex_poly_corner = static_cast( @@ -123,7 +123,7 @@ static void mask_data_init_mapping(SubdivCCGMaskEvaluator *mask_evaluator, const int ptex_face_index = 0; PolyCornerIndex *ptex_poly_corner = data->ptex_poly_corner; for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) { - const MPoly *poly = &mpoly[poly_index]; + const MPoly *poly = &polys[poly_index]; if (poly->totloop == 4) { ptex_poly_corner[ptex_face_index].poly_index = poly_index; ptex_poly_corner[ptex_face_index].corner = 0; @@ -142,7 +142,7 @@ static void mask_data_init_mapping(SubdivCCGMaskEvaluator *mask_evaluator, const static void mask_init_data(SubdivCCGMaskEvaluator *mask_evaluator, const Mesh *mesh) { GridPaintMaskData *data = static_cast(mask_evaluator->user_data); - data->mpoly = BKE_mesh_polys(mesh); + data->polys = mesh->polys(); data->grid_paint_mask = static_cast( CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK)); mask_data_init_mapping(mask_evaluator, mesh); diff --git a/source/blender/blenkernel/intern/subdiv_ccg_material.cc b/source/blender/blenkernel/intern/subdiv_ccg_material.cc index 23f5371b504..b1fe12ce8fe 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg_material.cc +++ b/source/blender/blenkernel/intern/subdiv_ccg_material.cc @@ -15,7 +15,7 @@ struct CCGMaterialFromMeshData { const Mesh *mesh; - const MPoly *polys; + blender::Span polys; const int *material_indices; }; @@ -45,7 +45,7 @@ void BKE_subdiv_ccg_material_flags_init_from_mesh( data->mesh = mesh; data->material_indices = (const int *)CustomData_get_layer_named( &mesh->pdata, CD_PROP_INT32, "material_index"); - data->polys = BKE_mesh_polys(mesh); + data->polys = mesh->polys(); material_flags_evaluator->eval_material_flags = subdiv_ccg_material_flags_eval; material_flags_evaluator->free = subdiv_ccg_material_flags_free; material_flags_evaluator->user_data = data; diff --git a/source/blender/blenkernel/intern/subdiv_converter_mesh.cc b/source/blender/blenkernel/intern/subdiv_converter_mesh.cc index 8be3f128e10..85cac62660d 100644 --- a/source/blender/blenkernel/intern/subdiv_converter_mesh.cc +++ b/source/blender/blenkernel/intern/subdiv_converter_mesh.cc @@ -35,9 +35,9 @@ struct ConverterStorage { SubdivSettings settings; const Mesh *mesh; const float (*vert_positions)[3]; - const MEdge *edges; - const MPoly *polys; - const MLoop *loops; + blender::Span edges; + blender::Span polys; + blender::Span loops; /* CustomData layer for vertex sharpnesses. */ const float *cd_vertex_crease; @@ -135,10 +135,10 @@ static void get_face_vertices(const OpenSubdiv_Converter *converter, { ConverterStorage *storage = static_cast(converter->user_data); const MPoly *poly = &storage->polys[manifold_face_index]; - const MLoop *mloop = storage->loops; + const blender::Span loops = storage->loops; for (int corner = 0; corner < poly->totloop; corner++) { manifold_face_vertices[corner] = - storage->manifold_vertex_index[mloop[poly->loopstart + corner].v]; + storage->manifold_vertex_index[loops[poly->loopstart + corner].v]; } } @@ -213,10 +213,10 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la MEM_malloc_arrayN(mesh->totloop, sizeof(int), "loop uv vertex index")); } UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create( - storage->polys, + storage->polys.data(), (const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"), (const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".select_poly"), - storage->loops, + storage->loops.data(), mloopuv, num_poly, num_vert, @@ -354,16 +354,16 @@ static void initialize_manifold_index_array(const BLI_bitmap *used_map, static void initialize_manifold_indices(ConverterStorage *storage) { const Mesh *mesh = storage->mesh; - const MEdge *medge = storage->edges; - const MLoop *mloop = storage->loops; - const MPoly *mpoly = storage->polys; + const blender::Span edges = storage->edges; + const blender::Span loops = storage->loops; + const blender::Span polys = storage->polys; /* Set bits of elements which are not loose. */ BLI_bitmap *vert_used_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map"); BLI_bitmap *edge_used_map = BLI_BITMAP_NEW(mesh->totedge, "edge used map"); for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) { - const MPoly *poly = &mpoly[poly_index]; + const MPoly *poly = &polys[poly_index]; for (int corner = 0; corner < poly->totloop; corner++) { - const MLoop *loop = &mloop[poly->loopstart + corner]; + const MLoop *loop = &loops[poly->loopstart + corner]; BLI_BITMAP_ENABLE(vert_used_map, loop->v); BLI_BITMAP_ENABLE(edge_used_map, loop->e); } @@ -382,7 +382,7 @@ static void initialize_manifold_indices(ConverterStorage *storage) storage->infinite_sharp_vertices_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map"); for (int edge_index = 0; edge_index < mesh->totedge; edge_index++) { if (!BLI_BITMAP_TEST_BOOL(edge_used_map, edge_index)) { - const MEdge *edge = &medge[edge_index]; + const MEdge *edge = &edges[edge_index]; BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge->v1); BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge->v2); } @@ -401,9 +401,9 @@ static void init_user_data(OpenSubdiv_Converter *converter, user_data->settings = *settings; user_data->mesh = mesh; user_data->vert_positions = BKE_mesh_vert_positions(mesh); - user_data->edges = BKE_mesh_edges(mesh); - user_data->polys = BKE_mesh_polys(mesh); - user_data->loops = BKE_mesh_loops(mesh); + user_data->edges = mesh->edges(); + user_data->polys = mesh->polys(); + user_data->loops = mesh->loops(); user_data->cd_vertex_crease = static_cast( CustomData_get_layer(&mesh->vdata, CD_CREASE)); user_data->cd_edge_crease = static_cast( diff --git a/source/blender/blenkernel/intern/subdiv_deform.cc b/source/blender/blenkernel/intern/subdiv_deform.cc index b5a0d6cb040..8214d0e7704 100644 --- a/source/blender/blenkernel/intern/subdiv_deform.cc +++ b/source/blender/blenkernel/intern/subdiv_deform.cc @@ -18,8 +18,8 @@ #include "BKE_customdata.h" #include "BKE_subdiv.h" #include "BKE_subdiv_eval.h" -#include "BKE_subdiv_foreach.h" -#include "BKE_subdiv_mesh.h" +#include "BKE_subdiv_foreach.hh" +#include "BKE_subdiv_mesh.hh" #include "MEM_guardedalloc.h" diff --git a/source/blender/blenkernel/intern/subdiv_displacement_multires.cc b/source/blender/blenkernel/intern/subdiv_displacement_multires.cc index b076a4b7ad5..f4fe80276d4 100644 --- a/source/blender/blenkernel/intern/subdiv_displacement_multires.cc +++ b/source/blender/blenkernel/intern/subdiv_displacement_multires.cc @@ -35,7 +35,7 @@ struct MultiresDisplacementData { /* Mesh is used to read external displacement. */ Mesh *mesh; const MultiresModifierData *mmd; - const MPoly *mpoly; + blender::Span polys; const MDisps *mdisps; /* Indexed by ptex face index, contains polygon/corner which corresponds * to it. @@ -71,7 +71,7 @@ static int displacement_get_grid_and_coord(SubdivDisplacement *displacement, MultiresDisplacementData *data = static_cast( displacement->user_data); const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index]; - const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + const MPoly *poly = &data->polys[poly_corner->poly_index]; const int start_grid_index = poly->loopstart + poly_corner->corner; int corner = 0; if (poly->totloop == 4) { @@ -95,7 +95,7 @@ static const MDisps *displacement_get_other_grid(SubdivDisplacement *displacemen MultiresDisplacementData *data = static_cast( displacement->user_data); const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index]; - const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + const MPoly *poly = &data->polys[poly_corner->poly_index]; const int effective_corner = (poly->totloop == 4) ? corner : poly_corner->corner; const int next_corner = (effective_corner + corner_delta + poly->totloop) % poly->totloop; return &data->mdisps[poly->loopstart + next_corner]; @@ -174,7 +174,7 @@ static void average_read_displacement_object(MultiresDisplacementData *data, float r_D[3]) { const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index]; - const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + const MPoly *poly = &data->polys[poly_corner->poly_index]; /* Get (u, v) coordinate within the other ptex face which corresponds to * the grid coordinates. */ float u, v; @@ -199,10 +199,10 @@ static void average_get_other_ptex_and_corner(MultiresDisplacementData *data, int *r_other_corner_index) { const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index]; - const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + const MPoly *poly = &data->polys[poly_corner->poly_index]; const int num_corners = poly->totloop; const bool is_quad = (num_corners == 4); - const int poly_index = poly - data->mpoly; + const int poly_index = poly - data->polys.data(); const int start_ptex_face_index = data->face_ptex_offset[poly_index]; *r_other_corner_index = (corner + corner_delta + num_corners) % num_corners; *r_other_ptex_face_index = is_quad ? start_ptex_face_index : @@ -249,7 +249,7 @@ static void average_with_all(SubdivDisplacement *displacement, MultiresDisplacementData *data = static_cast( displacement->user_data); const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index]; - const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + const MPoly *poly = &data->polys[poly_corner->poly_index]; const int num_corners = poly->totloop; for (int corner_delta = 1; corner_delta < num_corners; corner_delta++) { average_with_other(displacement, ptex_face_index, corner, 0.0f, 0.0f, corner_delta, r_D); @@ -305,7 +305,7 @@ static int displacement_get_face_corner(MultiresDisplacementData *data, const float v) { const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index]; - const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + const MPoly *poly = &data->polys[poly_corner->poly_index]; const int num_corners = poly->totloop; const bool is_quad = (num_corners == 4); if (is_quad) { @@ -368,9 +368,9 @@ static void free_displacement(SubdivDisplacement *displacement) static int count_num_ptex_faces(const Mesh *mesh) { int num_ptex_faces = 0; - const MPoly *mpoly = BKE_mesh_polys(mesh); + const blender::Span polys = mesh->polys(); for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) { - const MPoly *poly = &mpoly[poly_index]; + const MPoly *poly = &polys[poly_index]; num_ptex_faces += (poly->totloop == 4) ? 1 : poly->totloop; } return num_ptex_faces; @@ -380,7 +380,7 @@ static void displacement_data_init_mapping(SubdivDisplacement *displacement, con { MultiresDisplacementData *data = static_cast( displacement->user_data); - const MPoly *mpoly = BKE_mesh_polys(mesh); + const blender::Span polys = mesh->polys(); const int num_ptex_faces = count_num_ptex_faces(mesh); /* Allocate memory. */ data->ptex_poly_corner = static_cast( @@ -389,7 +389,7 @@ static void displacement_data_init_mapping(SubdivDisplacement *displacement, con int ptex_face_index = 0; PolyCornerIndex *ptex_poly_corner = data->ptex_poly_corner; for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) { - const MPoly *poly = &mpoly[poly_index]; + const MPoly *poly = &polys[poly_index]; if (poly->totloop == 4) { ptex_poly_corner[ptex_face_index].poly_index = poly_index; ptex_poly_corner[ptex_face_index].corner = 0; @@ -416,7 +416,7 @@ static void displacement_init_data(SubdivDisplacement *displacement, data->grid_size = BKE_subdiv_grid_size_from_level(mmd->totlvl); data->mesh = mesh; data->mmd = mmd; - data->mpoly = BKE_mesh_polys(mesh); + data->polys = mesh->polys(); data->mdisps = static_cast(CustomData_get_layer(&mesh->ldata, CD_MDISPS)); data->face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv); data->is_initialized = false; diff --git a/source/blender/blenkernel/intern/subdiv_eval.cc b/source/blender/blenkernel/intern/subdiv_eval.cc index b8cda67f642..b7d79e88c26 100644 --- a/source/blender/blenkernel/intern/subdiv_eval.cc +++ b/source/blender/blenkernel/intern/subdiv_eval.cc @@ -82,17 +82,17 @@ static void set_coarse_positions(Subdiv *subdiv, const float (*coarse_vertex_cos)[3]) { const float(*positions)[3] = BKE_mesh_vert_positions(mesh); - const MPoly *mpoly = BKE_mesh_polys(mesh); - const MLoop *mloop = BKE_mesh_loops(mesh); + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); /* Mark vertices which needs new coordinates. */ /* TODO(sergey): This is annoying to calculate this on every update, * maybe it's better to cache this mapping. Or make it possible to have * OpenSubdiv's vertices match mesh ones? */ BLI_bitmap *vertex_used_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map"); for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) { - const MPoly *poly = &mpoly[poly_index]; + const MPoly *poly = &polys[poly_index]; for (int corner = 0; corner < poly->totloop; corner++) { - const MLoop *loop = &mloop[poly->loopstart + corner]; + const MLoop *loop = &loops[poly->loopstart + corner]; BLI_BITMAP_ENABLE(vertex_used_map, loop->v); } } @@ -126,7 +126,7 @@ static void set_coarse_positions(Subdiv *subdiv, struct FaceVaryingDataFromUVContext { OpenSubdiv_TopologyRefiner *topology_refiner; const Mesh *mesh; - const MPoly *polys; + blender::Span polys; const float (*mloopuv)[2]; float (*buffer)[2]; int layer_index; @@ -173,7 +173,7 @@ static void set_face_varying_data_from_uv(Subdiv *subdiv, ctx.layer_index = layer_index; ctx.mloopuv = mluv; ctx.mesh = mesh; - ctx.polys = BKE_mesh_polys(mesh); + ctx.polys = mesh->polys(); ctx.buffer = buffer; TaskParallelSettings parallel_range_settings; diff --git a/source/blender/blenkernel/intern/subdiv_foreach.cc b/source/blender/blenkernel/intern/subdiv_foreach.cc index 86a1c18c416..e1ef4cdc15f 100644 --- a/source/blender/blenkernel/intern/subdiv_foreach.cc +++ b/source/blender/blenkernel/intern/subdiv_foreach.cc @@ -5,7 +5,7 @@ * \ingroup bke */ -#include "BKE_subdiv_foreach.h" +#include "BKE_subdiv_foreach.hh" #include "atomic_ops.h" @@ -20,7 +20,7 @@ #include "BKE_key.h" #include "BKE_mesh.h" #include "BKE_subdiv.h" -#include "BKE_subdiv_mesh.h" +#include "BKE_subdiv_mesh.hh" #include "MEM_guardedalloc.h" @@ -67,9 +67,9 @@ BLI_INLINE int ptex_face_resolution_get(const MPoly *poly, int resolution) struct SubdivForeachTaskContext { const Mesh *coarse_mesh; - const MEdge *coarse_edges; - const MPoly *coarse_polys; - const MLoop *coarse_loops; + blender::Span coarse_edges; + blender::Span coarse_polys; + blender::Span coarse_loops; const SubdivToMeshSettings *settings; /* Callbacks. */ const SubdivForeachContext *foreach_context; @@ -300,7 +300,7 @@ static void subdiv_foreach_corner_vertices_regular_do( bool check_usage) { const float weights[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}}; - const int coarse_poly_index = coarse_poly - ctx->coarse_polys; + const int coarse_poly_index = coarse_poly - ctx->coarse_polys.data(); const int ptex_face_index = ctx->face_ptex_offset[coarse_poly_index]; for (int corner = 0; corner < coarse_poly->totloop; corner++) { const MLoop *coarse_loop = &ctx->coarse_loops[coarse_poly->loopstart + corner]; @@ -339,7 +339,7 @@ static void subdiv_foreach_corner_vertices_special_do( SubdivForeachVertexFromCornerCb vertex_corner, bool check_usage) { - const int coarse_poly_index = coarse_poly - ctx->coarse_polys; + const int coarse_poly_index = coarse_poly - ctx->coarse_polys.data(); int ptex_face_index = ctx->face_ptex_offset[coarse_poly_index]; for (int corner = 0; corner < coarse_poly->totloop; corner++, ptex_face_index++) { const MLoop *coarse_loop = &ctx->coarse_loops[coarse_poly->loopstart + corner]; @@ -426,7 +426,7 @@ static void subdiv_foreach_edge_vertices_regular_do(SubdivForeachTaskContext *ct const int resolution_1 = resolution - 1; const float inv_resolution_1 = 1.0f / float(resolution_1); const int num_subdiv_vertices_per_coarse_edge = resolution - 2; - const int coarse_poly_index = coarse_poly - ctx->coarse_polys; + const int coarse_poly_index = coarse_poly - ctx->coarse_polys.data(); const int ptex_face_index = ctx->face_ptex_offset[coarse_poly_index]; for (int corner = 0; corner < coarse_poly->totloop; corner++) { const MLoop *coarse_loop = &ctx->coarse_loops[coarse_poly->loopstart + corner]; @@ -488,7 +488,7 @@ static void subdiv_foreach_edge_vertices_special_do(SubdivForeachTaskContext *ct const int num_subdiv_vertices_per_coarse_edge = resolution - 2; const int num_vertices_per_ptex_edge = ((resolution >> 1) + 1); const float inv_ptex_resolution_1 = 1.0f / float(num_vertices_per_ptex_edge - 1); - const int coarse_poly_index = coarse_poly - ctx->coarse_polys; + const int coarse_poly_index = coarse_poly - ctx->coarse_polys.data(); const int ptex_face_start_index = ctx->face_ptex_offset[coarse_poly_index]; int ptex_face_index = ptex_face_start_index; for (int corner = 0; corner < coarse_poly->totloop; corner++, ptex_face_index++) { @@ -599,7 +599,7 @@ static void subdiv_foreach_inner_vertices_regular(SubdivForeachTaskContext *ctx, { const int resolution = ctx->settings->resolution; const float inv_resolution_1 = 1.0f / float(resolution - 1); - const int coarse_poly_index = coarse_poly - ctx->coarse_polys; + const int coarse_poly_index = coarse_poly - ctx->coarse_polys.data(); const int ptex_face_index = ctx->face_ptex_offset[coarse_poly_index]; const int start_vertex_index = ctx->subdiv_vertex_offset[coarse_poly_index]; int subdiv_vertex_index = ctx->vertices_inner_offset + start_vertex_index; @@ -626,7 +626,7 @@ static void subdiv_foreach_inner_vertices_special(SubdivForeachTaskContext *ctx, const int resolution = ctx->settings->resolution; const int ptex_face_resolution = ptex_face_resolution_get(coarse_poly, resolution); const float inv_ptex_face_resolution_1 = 1.0f / float(ptex_face_resolution - 1); - const int coarse_poly_index = coarse_poly - ctx->coarse_polys; + const int coarse_poly_index = coarse_poly - ctx->coarse_polys.data(); int ptex_face_index = ctx->face_ptex_offset[coarse_poly_index]; const int start_vertex_index = ctx->subdiv_vertex_offset[coarse_poly_index]; int subdiv_vertex_index = ctx->vertices_inner_offset + start_vertex_index; @@ -753,7 +753,7 @@ static void subdiv_foreach_edges_all_patches_regular(SubdivForeachTaskContext *c void *tls, const MPoly *coarse_poly) { - const int poly_index = coarse_poly - ctx->coarse_polys; + const int poly_index = coarse_poly - ctx->coarse_polys.data(); const int resolution = ctx->settings->resolution; const int start_vertex_index = ctx->vertices_inner_offset + ctx->subdiv_vertex_offset[poly_index]; @@ -830,7 +830,7 @@ static void subdiv_foreach_edges_all_patches_special(SubdivForeachTaskContext *c void *tls, const MPoly *coarse_poly) { - const int poly_index = coarse_poly - ctx->coarse_polys; + const int poly_index = coarse_poly - ctx->coarse_polys.data(); const int resolution = ctx->settings->resolution; const int ptex_face_resolution = ptex_face_resolution_get(coarse_poly, resolution); const int ptex_face_inner_resolution = ptex_face_resolution - 2; @@ -1091,7 +1091,7 @@ static void subdiv_foreach_loops_regular(SubdivForeachTaskContext *ctx, { const int resolution = ctx->settings->resolution; /* Base/coarse mesh information. */ - const int coarse_poly_index = coarse_poly - ctx->coarse_polys; + const int coarse_poly_index = coarse_poly - ctx->coarse_polys.data(); const int ptex_resolution = ptex_face_resolution_get(coarse_poly, resolution); const int ptex_inner_resolution = ptex_resolution - 2; const int num_subdiv_edges_per_coarse_edge = resolution - 1; @@ -1282,7 +1282,7 @@ static void subdiv_foreach_loops_special(SubdivForeachTaskContext *ctx, { const int resolution = ctx->settings->resolution; /* Base/coarse mesh information. */ - const int coarse_poly_index = coarse_poly - ctx->coarse_polys; + const int coarse_poly_index = coarse_poly - ctx->coarse_polys.data(); const int ptex_face_resolution = ptex_face_resolution_get(coarse_poly, resolution); const int ptex_face_inner_resolution = ptex_face_resolution - 2; const float inv_ptex_resolution_1 = 1.0f / float(ptex_face_resolution - 1); @@ -1808,9 +1808,9 @@ bool BKE_subdiv_foreach_subdiv_geometry(Subdiv *subdiv, { SubdivForeachTaskContext ctx = {0}; ctx.coarse_mesh = coarse_mesh; - ctx.coarse_edges = BKE_mesh_edges(coarse_mesh); - ctx.coarse_polys = BKE_mesh_polys(coarse_mesh); - ctx.coarse_loops = BKE_mesh_loops(coarse_mesh); + ctx.coarse_edges = coarse_mesh->edges(); + ctx.coarse_polys = coarse_mesh->polys(); + ctx.coarse_loops = coarse_mesh->loops(); ctx.settings = mesh_settings; ctx.foreach_context = context; subdiv_foreach_ctx_init(subdiv, &ctx); diff --git a/source/blender/blenkernel/intern/subdiv_mesh.cc b/source/blender/blenkernel/intern/subdiv_mesh.cc index ee9e02883c6..a1e137f6000 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.cc +++ b/source/blender/blenkernel/intern/subdiv_mesh.cc @@ -24,8 +24,8 @@ #include "BKE_mesh_mapping.h" #include "BKE_subdiv.h" #include "BKE_subdiv_eval.h" -#include "BKE_subdiv_foreach.h" -#include "BKE_subdiv_mesh.h" +#include "BKE_subdiv_foreach.hh" +#include "BKE_subdiv_mesh.hh" #include "MEM_guardedalloc.h" @@ -41,16 +41,16 @@ struct SubdivMeshContext { const SubdivToMeshSettings *settings; const Mesh *coarse_mesh; const float (*coarse_positions)[3]; - const MEdge *coarse_edges; - const MPoly *coarse_polys; - const MLoop *coarse_loops; + blender::Span coarse_edges; + blender::Span coarse_polys; + blender::Span coarse_loops; Subdiv *subdiv; Mesh *subdiv_mesh; - float3 *subdiv_positions; - MEdge *subdiv_edges; - MPoly *subdiv_polys; - MLoop *subdiv_loops; + blender::MutableSpan subdiv_positions; + blender::MutableSpan subdiv_edges; + blender::MutableSpan subdiv_polys; + blender::MutableSpan subdiv_loops; /* Cached custom data arrays for faster access. */ int *vert_origindex; @@ -87,10 +87,10 @@ static void subdiv_mesh_ctx_cache_uv_layers(SubdivMeshContext *ctx) static void subdiv_mesh_ctx_cache_custom_data_layers(SubdivMeshContext *ctx) { Mesh *subdiv_mesh = ctx->subdiv_mesh; - ctx->subdiv_positions = subdiv_mesh->vert_positions_for_write().data(); - ctx->subdiv_edges = BKE_mesh_edges_for_write(subdiv_mesh); - ctx->subdiv_polys = BKE_mesh_polys_for_write(subdiv_mesh); - ctx->subdiv_loops = BKE_mesh_loops_for_write(subdiv_mesh); + ctx->subdiv_positions = subdiv_mesh->vert_positions_for_write(); + ctx->subdiv_edges = subdiv_mesh->edges_for_write(); + ctx->subdiv_polys = subdiv_mesh->polys_for_write(); + ctx->subdiv_loops = subdiv_mesh->loops_for_write(); /* Pointers to original indices layers. */ ctx->vert_origindex = static_cast( CustomData_get_layer_for_write(&subdiv_mesh->vdata, CD_ORIGINDEX, subdiv_mesh->totvert)); @@ -146,7 +146,6 @@ static void loops_of_ptex_get(const SubdivMeshContext *ctx, const MPoly *coarse_poly, const int ptex_of_poly_index) { - const MLoop *coarse_mloop = ctx->coarse_loops; const int first_ptex_loop_index = coarse_poly->loopstart + ptex_of_poly_index; /* Loop which look in the (opposite) V direction of the current * ptex face. @@ -155,8 +154,8 @@ static void loops_of_ptex_get(const SubdivMeshContext *ctx, const int last_ptex_loop_index = coarse_poly->loopstart + (ptex_of_poly_index + coarse_poly->totloop - 1) % coarse_poly->totloop; - loops_of_ptex->first_loop = &coarse_mloop[first_ptex_loop_index]; - loops_of_ptex->last_loop = &coarse_mloop[last_ptex_loop_index]; + loops_of_ptex->first_loop = &ctx->coarse_loops[first_ptex_loop_index]; + loops_of_ptex->last_loop = &ctx->coarse_loops[last_ptex_loop_index]; if (coarse_poly->totloop == 4) { loops_of_ptex->second_loop = loops_of_ptex->first_loop + 1; loops_of_ptex->third_loop = loops_of_ptex->first_loop + 2; @@ -202,13 +201,13 @@ static void vertex_interpolation_init(const SubdivMeshContext *ctx, const MPoly *coarse_poly) { const Mesh *coarse_mesh = ctx->coarse_mesh; - const MLoop *coarse_mloop = ctx->coarse_loops; + const blender::Span coarse_loops = ctx->coarse_loops; if (coarse_poly->totloop == 4) { vertex_interpolation->vertex_data = &coarse_mesh->vdata; - vertex_interpolation->vertex_indices[0] = coarse_mloop[coarse_poly->loopstart + 0].v; - vertex_interpolation->vertex_indices[1] = coarse_mloop[coarse_poly->loopstart + 1].v; - vertex_interpolation->vertex_indices[2] = coarse_mloop[coarse_poly->loopstart + 2].v; - vertex_interpolation->vertex_indices[3] = coarse_mloop[coarse_poly->loopstart + 3].v; + vertex_interpolation->vertex_indices[0] = coarse_loops[coarse_poly->loopstart + 0].v; + vertex_interpolation->vertex_indices[1] = coarse_loops[coarse_poly->loopstart + 1].v; + vertex_interpolation->vertex_indices[2] = coarse_loops[coarse_poly->loopstart + 2].v; + vertex_interpolation->vertex_indices[3] = coarse_loops[coarse_poly->loopstart + 3].v; vertex_interpolation->vertex_data_storage_allocated = false; } else { @@ -232,7 +231,7 @@ static void vertex_interpolation_init(const SubdivMeshContext *ctx, blender::Array indices(coarse_poly->totloop); for (int i = 0; i < coarse_poly->totloop; i++) { weights[i] = weight; - indices[i] = coarse_mloop[coarse_poly->loopstart + i].v; + indices[i] = coarse_loops[coarse_poly->loopstart + i].v; } CustomData_interp(&coarse_mesh->vdata, &vertex_interpolation->vertex_data_storage, @@ -254,13 +253,13 @@ static void vertex_interpolation_from_corner(const SubdivMeshContext *ctx, } else { const CustomData *vertex_data = &ctx->coarse_mesh->vdata; - const MLoop *coarse_mloop = ctx->coarse_loops; + const blender::Span coarse_loops = ctx->coarse_loops; LoopsOfPtex loops_of_ptex; loops_of_ptex_get(ctx, &loops_of_ptex, coarse_poly, corner); /* Ptex face corner corresponds to a poly loop with same index. */ CustomData_copy_data(vertex_data, &vertex_interpolation->vertex_data_storage, - coarse_mloop[coarse_poly->loopstart + corner].v, + coarse_loops[coarse_poly->loopstart + corner].v, 0, 1); /* Interpolate remaining ptex face corners, which hits loops @@ -269,16 +268,16 @@ static void vertex_interpolation_from_corner(const SubdivMeshContext *ctx, * TODO(sergey): Re-use one of interpolation results from previous * iteration. */ const float weights[2] = {0.5f, 0.5f}; - const int first_loop_index = loops_of_ptex.first_loop - coarse_mloop; - const int last_loop_index = loops_of_ptex.last_loop - coarse_mloop; + const int first_loop_index = loops_of_ptex.first_loop - coarse_loops.data(); + const int last_loop_index = loops_of_ptex.last_loop - coarse_loops.data(); const int first_indices[2] = { - int(coarse_mloop[first_loop_index].v), - int(coarse_mloop[coarse_poly->loopstart + + int(coarse_loops[first_loop_index].v), + int(coarse_loops[coarse_poly->loopstart + (first_loop_index - coarse_poly->loopstart + 1) % coarse_poly->totloop] .v)}; const int last_indices[2] = { - int(coarse_mloop[first_loop_index].v), - int(coarse_mloop[last_loop_index].v), + int(coarse_loops[first_loop_index].v), + int(coarse_loops[last_loop_index].v), }; CustomData_interp(vertex_data, &vertex_interpolation->vertex_data_storage, @@ -387,7 +386,7 @@ static void loop_interpolation_from_corner(const SubdivMeshContext *ctx, } else { const CustomData *loop_data = &ctx->coarse_mesh->ldata; - const MLoop *coarse_mloop = ctx->coarse_loops; + const blender::Span coarse_loops = ctx->coarse_loops; LoopsOfPtex loops_of_ptex; loops_of_ptex_get(ctx, &loops_of_ptex, coarse_poly, corner); /* Ptex face corner corresponds to a poly loop with same index. */ @@ -401,13 +400,13 @@ static void loop_interpolation_from_corner(const SubdivMeshContext *ctx, * iteration. */ const float weights[2] = {0.5f, 0.5f}; const int base_loop_index = coarse_poly->loopstart; - const int first_loop_index = loops_of_ptex.first_loop - coarse_mloop; + const int first_loop_index = loops_of_ptex.first_loop - coarse_loops.data(); const int second_loop_index = base_loop_index + (first_loop_index - base_loop_index + 1) % coarse_poly->totloop; const int first_indices[2] = {first_loop_index, second_loop_index}; const int last_indices[2] = { - int(loops_of_ptex.last_loop - coarse_mloop), - int(loops_of_ptex.first_loop - coarse_mloop), + int(loops_of_ptex.last_loop - coarse_loops.data()), + int(loops_of_ptex.first_loop - coarse_loops.data()), }; CustomData_interp( loop_data, &loop_interpolation->loop_data_storage, first_indices, weights, nullptr, 2, 1); @@ -813,11 +812,9 @@ static void subdiv_mesh_edge(const SubdivForeachContext *foreach_context, const int subdiv_v2) { SubdivMeshContext *ctx = static_cast(foreach_context->user_data); - MEdge *subdiv_medge = ctx->subdiv_edges; - MEdge *subdiv_edge = &subdiv_medge[subdiv_edge_index]; subdiv_copy_edge_data(ctx, subdiv_edge_index, coarse_edge_index); - subdiv_edge->v1 = subdiv_v1; - subdiv_edge->v2 = subdiv_v2; + ctx->subdiv_edges[subdiv_edge_index].v1 = subdiv_v1; + ctx->subdiv_edges[subdiv_edge_index].v2 = subdiv_v2; } /** \} */ @@ -832,7 +829,7 @@ static void subdiv_interpolate_loop_data(const SubdivMeshContext *ctx, const float u, const float v) { - const int subdiv_loop_index = subdiv_loop - ctx->subdiv_loops; + const int subdiv_loop_index = subdiv_loop - ctx->subdiv_loops.data(); const float weights[4] = {(1.0f - u) * (1.0f - v), u * (1.0f - v), u * v, (1.0f - u) * v}; CustomData_interp(loop_interpolation->loop_data, &ctx->subdiv_mesh->ldata, @@ -854,7 +851,7 @@ static void subdiv_eval_uv_layer(SubdivMeshContext *ctx, return; } Subdiv *subdiv = ctx->subdiv; - const int mloop_index = subdiv_loop - ctx->subdiv_loops; + const int mloop_index = subdiv_loop - ctx->subdiv_loops.data(); for (int layer_index = 0; layer_index < ctx->num_uv_layers; layer_index++) { BKE_subdiv_eval_face_varying( subdiv, layer_index, ptex_face_index, u, v, ctx->uv_layers[layer_index][mloop_index]); @@ -903,8 +900,7 @@ static void subdiv_mesh_loop(const SubdivForeachContext *foreach_context, { SubdivMeshContext *ctx = static_cast(foreach_context->user_data); SubdivMeshTLS *tls = static_cast(tls_v); - const MPoly *coarse_mpoly = ctx->coarse_polys; - const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index]; + const MPoly *coarse_poly = &ctx->coarse_polys[coarse_poly_index]; MLoop *subdiv_loop = &ctx->subdiv_loops[subdiv_loop_index]; subdiv_mesh_ensure_loop_interpolation(ctx, tls, coarse_poly, coarse_corner); subdiv_interpolate_loop_data(ctx, subdiv_loop, &tls->loop_interpolation, u, v); @@ -923,8 +919,8 @@ static void subdiv_copy_poly_data(const SubdivMeshContext *ctx, MPoly *subdiv_poly, const MPoly *coarse_poly) { - const int coarse_poly_index = coarse_poly - ctx->coarse_polys; - const int subdiv_poly_index = subdiv_poly - ctx->subdiv_polys; + const int coarse_poly_index = coarse_poly - ctx->coarse_polys.data(); + const int subdiv_poly_index = subdiv_poly - ctx->subdiv_polys.data(); CustomData_copy_data( &ctx->coarse_mesh->pdata, &ctx->subdiv_mesh->pdata, coarse_poly_index, subdiv_poly_index, 1); } @@ -1103,7 +1099,7 @@ static void subdiv_mesh_vertex_of_loose_edge(const SubdivForeachContext *foreach if (ctx->vert_to_edge_map == nullptr) { BKE_mesh_vert_edge_map_create(&ctx->vert_to_edge_map, &ctx->vert_to_edge_buffer, - ctx->coarse_edges, + ctx->coarse_edges.data(), coarse_mesh->totvert, ctx->coarse_mesh->totedge); } @@ -1116,7 +1112,7 @@ static void subdiv_mesh_vertex_of_loose_edge(const SubdivForeachContext *foreach } /* Interpolate coordinate. */ BKE_subdiv_mesh_interpolate_position_on_edge(ctx->coarse_positions, - ctx->coarse_edges, + ctx->coarse_edges.data(), ctx->vert_to_edge_map, coarse_edge_index, is_simple, @@ -1183,9 +1179,9 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv, subdiv_context.coarse_mesh = coarse_mesh; subdiv_context.coarse_positions = BKE_mesh_vert_positions(coarse_mesh); - subdiv_context.coarse_edges = BKE_mesh_edges(coarse_mesh); - subdiv_context.coarse_polys = BKE_mesh_polys(coarse_mesh); - subdiv_context.coarse_loops = BKE_mesh_loops(coarse_mesh); + subdiv_context.coarse_edges = coarse_mesh->edges(); + subdiv_context.coarse_polys = coarse_mesh->polys(); + subdiv_context.coarse_loops = coarse_mesh->loops(); subdiv_context.subdiv = subdiv; subdiv_context.have_displacement = (subdiv->displacement_evaluator != nullptr); diff --git a/source/blender/blenlib/BLI_bit_ref.hh b/source/blender/blenlib/BLI_bit_ref.hh new file mode 100644 index 00000000000..09f17a0bb93 --- /dev/null +++ b/source/blender/blenlib/BLI_bit_ref.hh @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * + * This file provides the basis for processing "indexed bits" (i.e. every bit has an index). + * The main purpose of this file is to define how bits are indexed within a memory buffer. + * For example, one has to define whether the first bit is the least or most significant bit and + * how endianness affect the bit order. + * + * The order is defined as follows: + * - Every indexed bit is part of an #BitInt. These ints are ordered by their address as usual. + * - Within each #BitInt, the bits are ordered from least to most significant. + */ + +#include "BLI_index_range.hh" +#include "BLI_utildefines.h" + +#include + +namespace blender::bits { + +/** Using a large integer type is better because then it's easier to process many bits at once. */ +using BitInt = uint64_t; +/** Number of bits that fit into #BitInt. */ +static constexpr int64_t BitsPerInt = int64_t(sizeof(BitInt) * 8); +/** Shift amount to get from a bit index to an int index. Equivalent to `log(BitsPerInt, 2)`. */ +static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(BitInt) >= 2) + (sizeof(BitInt) >= 4) + + (sizeof(BitInt) >= 8); +/** Bit mask containing a 1 for the last few bits that index a bit inside of an #BitInt. */ +static constexpr BitInt BitIndexMask = (BitInt(1) << BitToIntIndexShift) - 1; + +inline BitInt mask_first_n_bits(const int64_t n) +{ + BLI_assert(n >= 0); + BLI_assert(n <= BitsPerInt); + if (n == BitsPerInt) { + return BitInt(-1); + } + return (BitInt(1) << n) - 1; +} + +inline BitInt mask_last_n_bits(const int64_t n) +{ + return ~mask_first_n_bits(BitsPerInt - n); +} + +inline BitInt mask_range_bits(const int64_t start, const int64_t size) +{ + BLI_assert(start >= 0); + BLI_assert(size >= 0); + const int64_t end = start + size; + BLI_assert(end <= BitsPerInt); + if (end == BitsPerInt) { + return mask_last_n_bits(size); + } + return ((BitInt(1) << end) - 1) & ~((BitInt(1) << start) - 1); +} + +inline BitInt mask_single_bit(const int64_t bit_index) +{ + BLI_assert(bit_index >= 0); + BLI_assert(bit_index < BitsPerInt); + return BitInt(1) << bit_index; +} + +inline BitInt *int_containing_bit(BitInt *data, const int64_t bit_index) +{ + return data + (bit_index >> BitToIntIndexShift); +} + +inline const BitInt *int_containing_bit(const BitInt *data, const int64_t bit_index) +{ + return data + (bit_index >> BitToIntIndexShift); +} + +/** + * This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but + * not changed. + */ +class BitRef { + private: + /** Points to the exact integer that the bit is in. */ + const BitInt *int_; + /** All zeros except for a single one at the bit that is referenced. */ + BitInt mask_; + + friend class MutableBitRef; + + public: + BitRef() = default; + + /** + * Reference a specific bit in an array. Note that #data does *not* have to point to the + * exact integer the bit is in. + */ + BitRef(const BitInt *data, const int64_t bit_index) + { + int_ = int_containing_bit(data, bit_index); + mask_ = mask_single_bit(bit_index & BitIndexMask); + } + + /** + * Return true when the bit is currently 1 and false otherwise. + */ + bool test() const + { + const BitInt value = *int_; + const BitInt masked_value = value & mask_; + return masked_value != 0; + } + + operator bool() const + { + return this->test(); + } +}; + +/** + * Similar to #BitRef, but also allows changing the referenced bit. + */ +class MutableBitRef { + private: + /** Points to the integer that the bit is in. */ + BitInt *int_; + /** All zeros except for a single one at the bit that is referenced. */ + BitInt mask_; + + public: + MutableBitRef() = default; + + /** + * Reference a specific bit in an array. Note that #data does *not* have to point to the + * exact int the bit is in. + */ + MutableBitRef(BitInt *data, const int64_t bit_index) + { + int_ = int_containing_bit(data, bit_index); + mask_ = mask_single_bit(bit_index & BitIndexMask); + } + + /** + * Support implicitly casting to a read-only #BitRef. + */ + operator BitRef() const + { + BitRef bit_ref; + bit_ref.int_ = int_; + bit_ref.mask_ = mask_; + return bit_ref; + } + + /** + * Return true when the bit is currently 1 and false otherwise. + */ + bool test() const + { + const BitInt value = *int_; + const BitInt masked_value = value & mask_; + return masked_value != 0; + } + + operator bool() const + { + return this->test(); + } + + /** + * Change the bit to a 1. + */ + void set() + { + *int_ |= mask_; + } + + /** + * Change the bit to a 0. + */ + void reset() + { + *int_ &= ~mask_; + } + + /** + * Change the bit to a 1 if #value is true and 0 otherwise. If the value is highly unpredictable + * by the CPU branch predictor, it can be faster to use #set_branchless instead. + */ + void set(const bool value) + { + if (value) { + this->set(); + } + else { + this->reset(); + } + } + + /** + * Does the same as #set, but does not use a branch. This is faster when the input value is + * unpredictable for the CPU branch predictor (best case for this function is a uniform random + * distribution with 50% probability for true and false). If the value is predictable, this is + * likely slower than #set. + */ + void set_branchless(const bool value) + { + const BitInt value_int = BitInt(value); + BLI_assert(ELEM(value_int, 0, 1)); + const BitInt old = *int_; + *int_ = + /* Unset bit. */ + (~mask_ & old) + /* Optionally set it again. The -1 turns a 1 into `0x00...` and a 0 into `0xff...`. */ + | (mask_ & ~(value_int - 1)); + } +}; + +inline std::ostream &operator<<(std::ostream &stream, const BitRef &bit) +{ + return stream << (bit ? "1" : "0"); +} + +inline std::ostream &operator<<(std::ostream &stream, const MutableBitRef &bit) +{ + return stream << BitRef(bit); +} + +} // namespace blender::bits + +namespace blender { +using bits::BitRef; +using bits::MutableBitRef; +} // namespace blender diff --git a/source/blender/blenlib/BLI_bit_span.hh b/source/blender/blenlib/BLI_bit_span.hh new file mode 100644 index 00000000000..0ab69b7cc67 --- /dev/null +++ b/source/blender/blenlib/BLI_bit_span.hh @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_bit_ref.hh" +#include "BLI_index_range.hh" +#include "BLI_memory_utils.hh" + +namespace blender::bits { + +/** Base class for a const and non-const bit-iterator. */ +class BitIteratorBase { + protected: + const BitInt *data_; + int64_t bit_index_; + + public: + BitIteratorBase(const BitInt *data, const int64_t bit_index) : data_(data), bit_index_(bit_index) + { + } + + BitIteratorBase &operator++() + { + bit_index_++; + return *this; + } + + friend bool operator!=(const BitIteratorBase &a, const BitIteratorBase &b) + { + BLI_assert(a.data_ == b.data_); + return a.bit_index_ != b.bit_index_; + } +}; + +/** Allows iterating over the bits in a memory buffer. */ +class BitIterator : public BitIteratorBase { + public: + BitIterator(const BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index) + { + } + + BitRef operator*() const + { + return BitRef(data_, bit_index_); + } +}; + +/** Allows iterating over the bits in a memory buffer. */ +class MutableBitIterator : public BitIteratorBase { + public: + MutableBitIterator(BitInt *data, const int64_t bit_index) : BitIteratorBase(data, bit_index) + { + } + + MutableBitRef operator*() const + { + return MutableBitRef(const_cast(data_), bit_index_); + } +}; + +/** + * Similar to #Span, but references a range of bits instead of normal C++ types (which must be at + * least one byte large). Use #MutableBitSpan if the values are supposed to be modified. + * + * The beginning and end of a #BitSpan does *not* have to be at byte/int boundaries. It can start + * and end at any bit. + */ +class BitSpan { + private: + /** Base pointer to the integers containing the bits. The actual bit span might start at a much + * higher address when `bit_range_.start()` is large. */ + const BitInt *data_ = nullptr; + /** The range of referenced bits. */ + IndexRange bit_range_ = {0, 0}; + + public: + /** Construct an empty span. */ + BitSpan() = default; + + BitSpan(const BitInt *data, const int64_t size_in_bits) : data_(data), bit_range_(size_in_bits) + { + } + + BitSpan(const BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) + { + } + + /** Number of bits referenced by the span. */ + int64_t size() const + { + return bit_range_.size(); + } + + bool is_empty() const + { + return bit_range_.is_empty(); + } + + IndexRange index_range() const + { + return IndexRange(bit_range_.size()); + } + + BitRef operator[](const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < bit_range_.size()); + return {data_, bit_range_.start() + index}; + } + + BitSpan slice(const IndexRange range) const + { + return {data_, bit_range_.slice(range)}; + } + + const BitInt *data() const + { + return data_; + } + + const IndexRange &bit_range() const + { + return bit_range_; + } + + BitIterator begin() const + { + return {data_, bit_range_.start()}; + } + + BitIterator end() const + { + return {data_, bit_range_.one_after_last()}; + } +}; + +/** Same as #BitSpan, but also allows modifying the referenced bits. */ +class MutableBitSpan { + private: + BitInt *data_ = nullptr; + IndexRange bit_range_ = {0, 0}; + + public: + MutableBitSpan() = default; + + MutableBitSpan(BitInt *data, const int64_t size) : data_(data), bit_range_(size) + { + } + + MutableBitSpan(BitInt *data, const IndexRange bit_range) : data_(data), bit_range_(bit_range) + { + } + + int64_t size() const + { + return bit_range_.size(); + } + + bool is_empty() const + { + return bit_range_.is_empty(); + } + + IndexRange index_range() const + { + return IndexRange(bit_range_.size()); + } + + MutableBitRef operator[](const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < bit_range_.size()); + return {data_, bit_range_.start() + index}; + } + + MutableBitSpan slice(const IndexRange range) const + { + return {data_, bit_range_.slice(range)}; + } + + BitInt *data() const + { + return data_; + } + + const IndexRange &bit_range() const + { + return bit_range_; + } + + MutableBitIterator begin() const + { + return {data_, bit_range_.start()}; + } + + MutableBitIterator end() const + { + return {data_, bit_range_.one_after_last()}; + } + + operator BitSpan() const + { + return {data_, bit_range_}; + } + + /** Sets all referenced bits to 1. */ + void set_all() + { + const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); + { + BitInt &first_int = *int_containing_bit(data_, bit_range_.start()); + const BitInt first_int_mask = mask_range_bits(ranges.prefix.start() & BitIndexMask, + ranges.prefix.size()); + first_int |= first_int_mask; + } + { + BitInt *start = int_containing_bit(data_, ranges.aligned.start()); + const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt; + constexpr BitInt fill_value = BitInt(-1); + initialized_fill_n(start, ints_to_fill, fill_value); + } + { + BitInt &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); + const BitInt last_int_mask = mask_first_n_bits(ranges.suffix.size()); + last_int |= last_int_mask; + } + } + + /** Sets all referenced bits to 0. */ + void reset_all() + { + const AlignedIndexRanges ranges = split_index_range_by_alignment(bit_range_, BitsPerInt); + { + BitInt &first_int = *int_containing_bit(data_, bit_range_.start()); + const BitInt first_int_mask = mask_range_bits(ranges.prefix.start() & BitIndexMask, + ranges.prefix.size()); + first_int &= ~first_int_mask; + } + { + BitInt *start = int_containing_bit(data_, ranges.aligned.start()); + const int64_t ints_to_fill = ranges.aligned.size() / BitsPerInt; + constexpr BitInt fill_value = 0; + initialized_fill_n(start, ints_to_fill, fill_value); + } + { + BitInt &last_int = *int_containing_bit(data_, bit_range_.one_after_last() - 1); + const BitInt last_int_mask = mask_first_n_bits(ranges.suffix.size()); + last_int &= ~last_int_mask; + } + } + + /** Sets all referenced bits to either 0 or 1. */ + void set_all(const bool value) + { + if (value) { + this->set_all(); + } + else { + this->reset_all(); + } + } + + /** Same as #set_all to mirror #MutableSpan. */ + void fill(const bool value) + { + this->set_all(value); + } +}; + +inline std::ostream &operator<<(std::ostream &stream, const BitSpan &span) +{ + stream << "(Size: " << span.size() << ", "; + for (const BitRef bit : span) { + stream << bit; + } + stream << ")"; + return stream; +} + +inline std::ostream &operator<<(std::ostream &stream, const MutableBitSpan &span) +{ + return stream << BitSpan(span); +} + +} // namespace blender::bits + +namespace blender { +using bits::BitSpan; +using bits::MutableBitSpan; +} // namespace blender diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh index 237bdce1452..3c48d17703f 100644 --- a/source/blender/blenlib/BLI_bit_vector.hh +++ b/source/blender/blenlib/BLI_bit_vector.hh @@ -38,142 +38,11 @@ #include #include "BLI_allocator.hh" -#include "BLI_index_range.hh" -#include "BLI_memory_utils.hh" +#include "BLI_bit_span.hh" #include "BLI_span.hh" namespace blender::bits { -/** - * Using a large integer type is better because then it's easier to process many bits at once. - */ -using IntType = uint64_t; -static constexpr int64_t BitsPerInt = int64_t(sizeof(IntType) * 8); -static constexpr int64_t BitToIntIndexShift = 3 + (sizeof(IntType) >= 2) + (sizeof(IntType) >= 4) + - (sizeof(IntType) >= 8); -static constexpr IntType BitIndexMask = (IntType(1) << BitToIntIndexShift) - 1; - -/** - * This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but - * not changed. - */ -class BitRef { - private: - /** Points to the integer that the bit is in. */ - const IntType *ptr_; - /** All zeros except for a single one at the bit that is referenced. */ - IntType mask_; - - friend class MutableBitRef; - - public: - BitRef() = default; - - /** - * Reference a specific bit in an array. Note that #ptr does *not* have to point to the - * exact integer the bit is in. - */ - BitRef(const IntType *ptr, const int64_t bit_index) - { - ptr_ = ptr + (bit_index >> BitToIntIndexShift); - mask_ = IntType(1) << (bit_index & BitIndexMask); - } - - /** - * Return true when the bit is currently 1 and false otherwise. - */ - bool test() const - { - const IntType value = *ptr_; - const IntType masked_value = value & mask_; - return masked_value != 0; - } - - operator bool() const - { - return this->test(); - } -}; - -/** - * Similar to #BitRef, but also allows changing the referenced bit. - */ -class MutableBitRef { - private: - /** Points to the integer that the bit is in. */ - IntType *ptr_; - /** All zeros except for a single one at the bit that is referenced. */ - IntType mask_; - - public: - MutableBitRef() = default; - - /** - * Reference a specific bit in an array. Note that #ptr does *not* have to point to the - * exact int the bit is in. - */ - MutableBitRef(IntType *ptr, const int64_t bit_index) - { - ptr_ = ptr + (bit_index >> BitToIntIndexShift); - mask_ = IntType(1) << IntType(bit_index & BitIndexMask); - } - - /** - * Support implicitly casting to a read-only #BitRef. - */ - operator BitRef() const - { - BitRef bit_ref; - bit_ref.ptr_ = ptr_; - bit_ref.mask_ = mask_; - return bit_ref; - } - - /** - * Return true when the bit is currently 1 and false otherwise. - */ - bool test() const - { - const IntType value = *ptr_; - const IntType masked_value = value & mask_; - return masked_value != 0; - } - - operator bool() const - { - return this->test(); - } - - /** - * Change the bit to a 1. - */ - void set() - { - *ptr_ |= mask_; - } - - /** - * Change the bit to a 0. - */ - void reset() - { - *ptr_ &= ~mask_; - } - - /** - * Change the bit to a 1 if #value is true and 0 otherwise. - */ - void set(const bool value) - { - if (value) { - this->set(); - } - else { - this->reset(); - } - } -}; - template< /** * Number of bits that can be stored in the vector without doing an allocation. @@ -193,13 +62,13 @@ class BitVector { static constexpr int64_t IntsInInlineBuffer = required_ints_for_bits(InlineBufferCapacity); static constexpr int64_t BitsInInlineBuffer = IntsInInlineBuffer * BitsPerInt; - static constexpr int64_t AllocationAlignment = alignof(IntType); + static constexpr int64_t AllocationAlignment = alignof(BitInt); /** * Points to the first integer used by the vector. It might point to the memory in the inline * buffer. */ - IntType *data_; + BitInt *data_; /** Current size of the vector in bits. */ int64_t size_in_bits_; @@ -211,7 +80,7 @@ class BitVector { BLI_NO_UNIQUE_ADDRESS Allocator allocator_; /** Contains the bits as long as the vector is small enough. */ - BLI_NO_UNIQUE_ADDRESS TypedBuffer inline_buffer_; + BLI_NO_UNIQUE_ADDRESS TypedBuffer inline_buffer_; public: BitVector(Allocator allocator = {}) noexcept : allocator_(allocator) @@ -219,7 +88,7 @@ class BitVector { data_ = inline_buffer_; size_in_bits_ = 0; capacity_in_bits_ = BitsInInlineBuffer; - uninitialized_fill_n(data_, IntsInInlineBuffer, IntType(0)); + uninitialized_fill_n(data_, IntsInInlineBuffer, BitInt(0)); } BitVector(NoExceptConstructor, Allocator allocator = {}) noexcept : BitVector(allocator) @@ -236,8 +105,8 @@ class BitVector { } else { /* Allocate a new array because the inline buffer is too small. */ - data_ = static_cast( - allocator_.allocate(ints_to_copy * sizeof(IntType), AllocationAlignment, __func__)); + data_ = static_cast( + allocator_.allocate(ints_to_copy * sizeof(BitInt), AllocationAlignment, __func__)); capacity_in_bits_ = ints_to_copy * BitsPerInt; } size_in_bits_ = other.size_in_bits_; @@ -303,6 +172,16 @@ class BitVector { return move_assign_container(*this, std::move(other)); } + operator BitSpan() const + { + return {data_, IndexRange(size_in_bits_)}; + } + + operator MutableBitSpan() + { + return {data_, IndexRange(size_in_bits_)}; + } + /** * Number of bits in the bit vector. */ @@ -352,80 +231,24 @@ class BitVector { size_in_bits_++; } - class Iterator { - private: - const BitVector *vector_; - int64_t index_; - - public: - Iterator(const BitVector &vector, const int64_t index) : vector_(&vector), index_(index) - { - } - - Iterator &operator++() - { - index_++; - return *this; - } - - friend bool operator!=(const Iterator &a, const Iterator &b) - { - BLI_assert(a.vector_ == b.vector_); - return a.index_ != b.index_; - } - - BitRef operator*() const - { - return (*vector_)[index_]; - } - }; - - class MutableIterator { - private: - BitVector *vector_; - int64_t index_; - - public: - MutableIterator(BitVector &vector, const int64_t index) : vector_(&vector), index_(index) - { - } - - MutableIterator &operator++() - { - index_++; - return *this; - } - - friend bool operator!=(const MutableIterator &a, const MutableIterator &b) - { - BLI_assert(a.vector_ == b.vector_); - return a.index_ != b.index_; - } - - MutableBitRef operator*() const - { - return (*vector_)[index_]; - } - }; - - Iterator begin() const + BitIterator begin() const { - return {*this, 0}; + return {data_, 0}; } - Iterator end() const + BitIterator end() const { - return {*this, size_in_bits_}; + return {data_, size_in_bits_}; } - MutableIterator begin() + MutableBitIterator begin() { - return {*this, 0}; + return {data_, 0}; } - MutableIterator end() + MutableBitIterator end() { - return {*this, size_in_bits_}; + return {data_, size_in_bits_}; } /** @@ -441,31 +264,8 @@ class BitVector { } size_in_bits_ = new_size_in_bits; if (old_size_in_bits < new_size_in_bits) { - this->fill_range(IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits), value); - } - } - - /** - * Set #value for every element in #range. - */ - void fill_range(const IndexRange range, const bool value) - { - const AlignedIndexRanges aligned_ranges = split_index_range_by_alignment(range, BitsPerInt); - - /* Fill first few bits. */ - for (const int64_t i : aligned_ranges.prefix) { - (*this)[i].set(value); - } - - /* Fill entire ints at once. */ - const int64_t start_fill_int_index = aligned_ranges.aligned.start() / BitsPerInt; - const int64_t ints_to_fill = aligned_ranges.aligned.size() / BitsPerInt; - const IntType fill_value = value ? IntType(-1) : IntType(0); - initialized_fill_n(data_ + start_fill_int_index, ints_to_fill, fill_value); - - /* Fill bits in the end that don't cover a full int. */ - for (const int64_t i : aligned_ranges.suffix) { - (*this)[i].set(value); + MutableBitSpan(data_, IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits)) + .set_all(value); } } @@ -474,7 +274,7 @@ class BitVector { */ void fill(const bool value) { - this->fill_range(IndexRange(0, size_in_bits_), value); + MutableBitSpan(data_, size_in_bits_).set_all(value); } /** @@ -517,7 +317,7 @@ class BitVector { } BLI_NOINLINE void realloc_to_at_least(const int64_t min_capacity_in_bits, - const IntType initial_value_for_new_ints = 0x00) + const BitInt initial_value_for_new_ints = 0) { if (capacity_in_bits_ >= min_capacity_in_bits) { return; @@ -531,8 +331,8 @@ class BitVector { const int64_t new_capacity_in_ints = std::max(min_capacity_in_ints, min_new_capacity_in_ints); const int64_t ints_to_copy = this->used_ints_amount(); - IntType *new_data = static_cast(allocator_.allocate( - new_capacity_in_ints * sizeof(IntType), AllocationAlignment, __func__)); + BitInt *new_data = static_cast( + allocator_.allocate(new_capacity_in_ints * sizeof(BitInt), AllocationAlignment, __func__)); uninitialized_copy_n(data_, ints_to_copy, new_data); /* Always initialize new capacity even if it isn't used yet. That's necessary to avoid warnings * caused by using uninitialized memory. This happens when e.g. setting a clearing a bit in an @@ -562,7 +362,5 @@ class BitVector { } // namespace blender::bits namespace blender { -using bits::BitRef; using bits::BitVector; -using bits::MutableBitRef; } // namespace blender diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 0ff75ca16e5..9a10358dcea 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -142,6 +142,18 @@ double BLI_dir_free_space(const char *dir) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL( */ char *BLI_current_working_dir(char *dir, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); eFileAttributes BLI_file_attributes(const char *path); +/** + * Changes the current working directory to the provided path. + * + * Usage of this function is strongly discouraged as it is not thread safe. It will likely cause + * issues if there is an operation on another thread that does not expect the current working + * directory to change. This has been added to support USDZ export, which has a problematic + * "feature" described in this issue https://projects.blender.org/blender/blender/issues/99807. It + * will be removed if it is possible to resolve that issue upstream in the USD library. + * + * \return true on success, false otherwise. + */ +bool BLI_change_working_dir(const char *dir); /** \} */ diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index 7fb7085360b..a283d77eafc 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -304,6 +304,13 @@ void rotate_eul(float beul[3], char axis, float angle); /* Order independent. */ +/** + * Manipulate `eul` so it's close to `oldrot` while representing the same rotation + * with the aim of having the minimum difference between all axes. + * + * This is typically done so interpolating the values between two euler rotations + * doesn't add undesired rotation (even rotating multiple times around one axis). + */ void compatible_eul(float eul[3], const float oldrot[3]); void add_eul_euleul(float r_eul[3], float a[3], float b[3], short order); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 16635df908b..bcc25db100c 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -457,6 +457,8 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc + tests/BLI_bit_ref_test.cc + tests/BLI_bit_span_test.cc tests/BLI_bit_vector_test.cc tests/BLI_bitmap_test.cc tests/BLI_bounds_test.cc diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index c74a0322ac5..dd35086ec21 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -504,8 +504,9 @@ void _va_mul_m3_series_4(float r[3][3], const float m2[3][3], const float m3[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); + float s[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(r, s, m3); } void _va_mul_m3_series_5(float r[3][3], const float m1[3][3], @@ -513,9 +514,11 @@ void _va_mul_m3_series_5(float r[3][3], const float m3[3][3], const float m4[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); - mul_m3_m3m3(r, r, m4); + float s[3][3]; + float t[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(t, s, m3); + mul_m3_m3m3(r, t, m4); } void _va_mul_m3_series_6(float r[3][3], const float m1[3][3], @@ -524,10 +527,12 @@ void _va_mul_m3_series_6(float r[3][3], const float m4[3][3], const float m5[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); - mul_m3_m3m3(r, r, m4); - mul_m3_m3m3(r, r, m5); + float s[3][3]; + float t[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(t, s, m3); + mul_m3_m3m3(s, t, m4); + mul_m3_m3m3(r, s, m5); } void _va_mul_m3_series_7(float r[3][3], const float m1[3][3], @@ -537,11 +542,13 @@ void _va_mul_m3_series_7(float r[3][3], const float m5[3][3], const float m6[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); - mul_m3_m3m3(r, r, m4); - mul_m3_m3m3(r, r, m5); - mul_m3_m3m3(r, r, m6); + float s[3][3]; + float t[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(t, s, m3); + mul_m3_m3m3(s, t, m4); + mul_m3_m3m3(t, s, m5); + mul_m3_m3m3(r, t, m6); } void _va_mul_m3_series_8(float r[3][3], const float m1[3][3], @@ -552,12 +559,14 @@ void _va_mul_m3_series_8(float r[3][3], const float m6[3][3], const float m7[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); - mul_m3_m3m3(r, r, m4); - mul_m3_m3m3(r, r, m5); - mul_m3_m3m3(r, r, m6); - mul_m3_m3m3(r, r, m7); + float s[3][3]; + float t[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(t, s, m3); + mul_m3_m3m3(s, t, m4); + mul_m3_m3m3(t, s, m5); + mul_m3_m3m3(s, t, m6); + mul_m3_m3m3(r, s, m7); } void _va_mul_m3_series_9(float r[3][3], const float m1[3][3], @@ -569,13 +578,15 @@ void _va_mul_m3_series_9(float r[3][3], const float m7[3][3], const float m8[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); - mul_m3_m3m3(r, r, m4); - mul_m3_m3m3(r, r, m5); - mul_m3_m3m3(r, r, m6); - mul_m3_m3m3(r, r, m7); - mul_m3_m3m3(r, r, m8); + float s[3][3]; + float t[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(t, s, m3); + mul_m3_m3m3(s, t, m4); + mul_m3_m3m3(t, s, m5); + mul_m3_m3m3(s, t, m6); + mul_m3_m3m3(t, s, m7); + mul_m3_m3m3(r, t, m8); } /** \} */ @@ -593,8 +604,9 @@ void _va_mul_m4_series_4(float r[4][4], const float m2[4][4], const float m3[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); + float s[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(r, s, m3); } void _va_mul_m4_series_5(float r[4][4], const float m1[4][4], @@ -602,9 +614,11 @@ void _va_mul_m4_series_5(float r[4][4], const float m3[4][4], const float m4[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); - mul_m4_m4m4(r, r, m4); + float s[4][4]; + float t[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(t, s, m3); + mul_m4_m4m4(r, t, m4); } void _va_mul_m4_series_6(float r[4][4], const float m1[4][4], @@ -613,10 +627,12 @@ void _va_mul_m4_series_6(float r[4][4], const float m4[4][4], const float m5[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); - mul_m4_m4m4(r, r, m4); - mul_m4_m4m4(r, r, m5); + float s[4][4]; + float t[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(t, s, m3); + mul_m4_m4m4(s, t, m4); + mul_m4_m4m4(r, s, m5); } void _va_mul_m4_series_7(float r[4][4], const float m1[4][4], @@ -626,11 +642,13 @@ void _va_mul_m4_series_7(float r[4][4], const float m5[4][4], const float m6[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); - mul_m4_m4m4(r, r, m4); - mul_m4_m4m4(r, r, m5); - mul_m4_m4m4(r, r, m6); + float s[4][4]; + float t[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(t, s, m3); + mul_m4_m4m4(s, t, m4); + mul_m4_m4m4(t, s, m5); + mul_m4_m4m4(r, t, m6); } void _va_mul_m4_series_8(float r[4][4], const float m1[4][4], @@ -641,12 +659,14 @@ void _va_mul_m4_series_8(float r[4][4], const float m6[4][4], const float m7[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); - mul_m4_m4m4(r, r, m4); - mul_m4_m4m4(r, r, m5); - mul_m4_m4m4(r, r, m6); - mul_m4_m4m4(r, r, m7); + float s[4][4]; + float t[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(t, s, m3); + mul_m4_m4m4(s, t, m4); + mul_m4_m4m4(t, s, m5); + mul_m4_m4m4(s, t, m6); + mul_m4_m4m4(r, s, m7); } void _va_mul_m4_series_9(float r[4][4], const float m1[4][4], @@ -658,13 +678,15 @@ void _va_mul_m4_series_9(float r[4][4], const float m7[4][4], const float m8[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); - mul_m4_m4m4(r, r, m4); - mul_m4_m4m4(r, r, m5); - mul_m4_m4m4(r, r, m6); - mul_m4_m4m4(r, r, m7); - mul_m4_m4m4(r, r, m8); + float s[4][4]; + float t[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(t, s, m3); + mul_m4_m4m4(s, t, m4); + mul_m4_m4m4(t, s, m5); + mul_m4_m4m4(s, t, m6); + mul_m4_m4m4(t, s, m7); + mul_m4_m4m4(r, t, m8); } /** \} */ diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index f47bc2d8ed7..49e6b61fad5 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -1490,15 +1490,18 @@ void rotate_eul(float beul[3], const char axis, const float angle) void compatible_eul(float eul[3], const float oldrot[3]) { - /* we could use M_PI as pi_thresh: which is correct but 5.1 gives better results. - * Checked with baking actions to fcurves - campbell */ - const float pi_thresh = (5.1f); + /* When the rotation exceeds 180 degrees, it can be wrapped by 360 degrees + * to produce a closer match. + * NOTE: Values between `pi` & `2 * pi` work, where `pi` has the lowest number of + * discontinuities and values approaching `2 * pi` center the resulting rotation around zero, + * at the expense of the result being less compatible, see !104856. */ + const float pi_thresh = (float)M_PI; const float pi_x2 = (2.0f * (float)M_PI); float deul[3]; uint i; - /* correct differences of about 360 degrees first */ + /* Correct differences around 360 degrees first. */ for (i = 0; i < 3; i++) { deul[i] = eul[i] - oldrot[i]; if (deul[i] > pi_thresh) { @@ -1511,29 +1514,17 @@ void compatible_eul(float eul[3], const float oldrot[3]) } } - /* is 1 of the axis rotations larger than 180 degrees and the other small? NO ELSE IF!! */ - if (fabsf(deul[0]) > 3.2f && fabsf(deul[1]) < 1.6f && fabsf(deul[2]) < 1.6f) { - if (deul[0] > 0.0f) { - eul[0] -= pi_x2; - } - else { - eul[0] += pi_x2; - } - } - if (fabsf(deul[1]) > 3.2f && fabsf(deul[2]) < 1.6f && fabsf(deul[0]) < 1.6f) { - if (deul[1] > 0.0f) { - eul[1] -= pi_x2; - } - else { - eul[1] += pi_x2; - } - } - if (fabsf(deul[2]) > 3.2f && fabsf(deul[0]) < 1.6f && fabsf(deul[1]) < 1.6f) { - if (deul[2] > 0.0f) { - eul[2] -= pi_x2; - } - else { - eul[2] += pi_x2; + uint j = 1, k = 2; + for (i = 0; i < 3; j = k, k = i++) { + /* Check if this axis of rotations larger than 180 degrees and + * the others are smaller than 90 degrees. */ + if (fabsf(deul[i]) > M_PI && fabsf(deul[j]) < M_PI_2 && fabsf(deul[k]) < M_PI_2) { + if (deul[i] > 0.0f) { + eul[i] -= pi_x2; + } + else { + eul[i] += pi_x2; + } } } } diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c index 64f4a0b1a26..f0b446e589b 100644 --- a/source/blender/blenlib/intern/storage.c +++ b/source/blender/blenlib/intern/storage.c @@ -54,11 +54,36 @@ #include "BLI_linklist.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_threads.h" #include "BLI_utildefines.h" +#if !defined(__APPLE__) +/* The implementation for Apple lives in storage_apple.mm.*/ +bool BLI_change_working_dir(const char *dir) +{ + BLI_assert(BLI_thread_is_main()); + + if (!BLI_is_dir(dir)) { + return false; + } +# if defined(WIN32) + wchar_t wdir[FILE_MAX]; + if (conv_utf_8_to_16(dir, wdir, ARRAY_SIZE(wdir)) != 0) { + return false; + } + return _wchdir(wdir) == 0; +# else + int result = chdir(dir); + if (result == 0) { + BLI_setenv("PWD", dir); + } + return result == 0; +# endif +} + char *BLI_current_working_dir(char *dir, const size_t maxncpy) { -#if defined(WIN32) +# if defined(WIN32) wchar_t path[MAX_PATH]; if (_wgetcwd(path, MAX_PATH)) { if (BLI_strncpy_wchar_as_utf8(dir, path, maxncpy) != maxncpy) { @@ -66,7 +91,7 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy) } } return NULL; -#else +# else const char *pwd = BLI_getenv("PWD"); if (pwd) { size_t srclen = BLI_strnlen(pwd, maxncpy); @@ -77,8 +102,9 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy) return NULL; } return getcwd(dir, maxncpy); -#endif +# endif } +#endif /* !defined (__APPLE__) */ double BLI_dir_free_space(const char *dir) { diff --git a/source/blender/blenlib/intern/storage_apple.mm b/source/blender/blenlib/intern/storage_apple.mm index aa62fcde463..e457c17adfd 100644 --- a/source/blender/blenlib/intern/storage_apple.mm +++ b/source/blender/blenlib/intern/storage_apple.mm @@ -13,6 +13,8 @@ #include "BLI_fileops.h" #include "BLI_path_util.h" +#include "BLI_string.h" + /* Extended file attribute used by OneDrive to mark placeholder files. */ static const char *ONEDRIVE_RECALLONOPEN_ATTRIBUTE = "com.microsoft.OneDrive.RecallOnOpen"; @@ -185,3 +187,27 @@ const char *BLI_expand_tilde(const char *path_with_tilde) } return path_expanded; } + +char *BLI_current_working_dir(char *dir, const size_t maxncpy) { + /* Can't just copy to the *dir pointer, as [path getCString gets grumpy.*/ + static char path_expanded[PATH_MAX]; + @autoreleasepool { + NSString *path = [[NSFileManager defaultManager] currentDirectoryPath]; + const size_t length = maxncpy > PATH_MAX ? PATH_MAX : maxncpy; + [path getCString:path_expanded maxLength:length encoding:NSUTF8StringEncoding]; + BLI_strncpy(dir, path_expanded, maxncpy); + return path_expanded; + } +} + +bool BLI_change_working_dir(const char* dir) { + @autoreleasepool { + NSString* path = [[NSString alloc] initWithUTF8String: dir]; + if ([[NSFileManager defaultManager] changeCurrentDirectoryPath: path] == YES) { + return true; + } + else { + return false; + } + } +} diff --git a/source/blender/blenlib/tests/BLI_bit_ref_test.cc b/source/blender/blenlib/tests/BLI_bit_ref_test.cc new file mode 100644 index 00000000000..2c880e4ea06 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_bit_ref_test.cc @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "BLI_bit_ref.hh" + +#include "testing/testing.h" + +namespace blender::bits::tests { + +TEST(bit_ref, MaskFirstNBits) +{ + EXPECT_EQ(mask_first_n_bits(0), 0); + EXPECT_EQ(mask_first_n_bits(1), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001); + EXPECT_EQ(mask_first_n_bits(5), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111); + EXPECT_EQ(mask_first_n_bits(63), + 0b0111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111); + EXPECT_EQ(mask_first_n_bits(64), + 0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111); +} + +TEST(bit_ref, MaskLastNBits) +{ + EXPECT_EQ(mask_last_n_bits(0), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_last_n_bits(1), + 0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_last_n_bits(5), + 0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_last_n_bits(63), + 0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1110); + EXPECT_EQ(mask_last_n_bits(64), + 0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111); +} + +TEST(bit_ref, MaskSingleBit) +{ + EXPECT_EQ(mask_single_bit(0), 1); + EXPECT_EQ(mask_single_bit(1), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010); + EXPECT_EQ(mask_single_bit(5), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000); + EXPECT_EQ(mask_single_bit(63), + 0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); +} + +TEST(bit_ref, IntContainingBit) +{ + std::array array; + uint64_t *data = array.data(); + EXPECT_EQ(int_containing_bit(data, 0), data); + EXPECT_EQ(int_containing_bit(data, 1), data); + EXPECT_EQ(int_containing_bit(data, 63), data); + EXPECT_EQ(int_containing_bit(data, 64), data + 1); + EXPECT_EQ(int_containing_bit(data, 65), data + 1); + EXPECT_EQ(int_containing_bit(data, 100), data + 1); + EXPECT_EQ(int_containing_bit(data, 127), data + 1); + EXPECT_EQ(int_containing_bit(data, 128), data + 2); + const uint64_t *data_const = data; + EXPECT_EQ(int_containing_bit(data_const, 0), data_const); + EXPECT_EQ(int_containing_bit(data_const, 1), data_const); + EXPECT_EQ(int_containing_bit(data_const, 63), data_const); + EXPECT_EQ(int_containing_bit(data_const, 64), data_const + 1); + EXPECT_EQ(int_containing_bit(data_const, 65), data_const + 1); + EXPECT_EQ(int_containing_bit(data_const, 100), data_const + 1); + EXPECT_EQ(int_containing_bit(data_const, 127), data_const + 1); + EXPECT_EQ(int_containing_bit(data_const, 128), data_const + 2); +} + +TEST(bit_ref, Test) +{ + uint64_t data = (1 << 3) | (1 << 7); + EXPECT_FALSE(BitRef(&data, 0).test()); + EXPECT_FALSE(BitRef(&data, 1).test()); + EXPECT_FALSE(BitRef(&data, 2).test()); + EXPECT_TRUE(BitRef(&data, 3).test()); + EXPECT_FALSE(BitRef(&data, 4)); + EXPECT_FALSE(BitRef(&data, 5)); + EXPECT_FALSE(BitRef(&data, 6)); + EXPECT_TRUE(BitRef(&data, 7)); + + EXPECT_FALSE(MutableBitRef(&data, 0).test()); + EXPECT_FALSE(MutableBitRef(&data, 1).test()); + EXPECT_FALSE(MutableBitRef(&data, 2).test()); + EXPECT_TRUE(MutableBitRef(&data, 3).test()); + EXPECT_FALSE(MutableBitRef(&data, 4)); + EXPECT_FALSE(MutableBitRef(&data, 5)); + EXPECT_FALSE(MutableBitRef(&data, 6)); + EXPECT_TRUE(MutableBitRef(&data, 7)); +} + +TEST(bit_ref, Set) +{ + uint64_t data = 0; + MutableBitRef(&data, 0).set(); + MutableBitRef(&data, 1).set(); + MutableBitRef(&data, 1).set(); + MutableBitRef(&data, 4).set(); + EXPECT_EQ(data, (1 << 0) | (1 << 1) | (1 << 4)); + MutableBitRef(&data, 5).set(true); + MutableBitRef(&data, 1).set(false); + EXPECT_EQ(data, (1 << 0) | (1 << 4) | (1 << 5)); +} + +TEST(bit_ref, Reset) +{ + uint64_t data = -1; + MutableBitRef(&data, 0).reset(); + MutableBitRef(&data, 2).reset(); + EXPECT_EQ(data, uint64_t(-1) & ~(1 << 0) & ~(1 << 2)); +} + +TEST(bit_ref, SetBranchless) +{ + uint64_t data = 0; + MutableBitRef(&data, 0).set_branchless(true); + EXPECT_EQ(data, 1); + MutableBitRef(&data, 0).set_branchless(false); + EXPECT_EQ(data, 0); + MutableBitRef(&data, 3).set_branchless(false); + MutableBitRef(&data, 4).set_branchless(true); + EXPECT_EQ(data, 16); + MutableBitRef(&data, 3).set_branchless(true); + MutableBitRef(&data, 4).set_branchless(true); + EXPECT_EQ(data, 24); +} + +TEST(bit_ref, Cast) +{ + uint64_t data = 0; + MutableBitRef mutable_ref(&data, 3); + BitRef ref = mutable_ref; + EXPECT_FALSE(ref); + mutable_ref.set(); + EXPECT_TRUE(ref); +} + +TEST(bit_ref, MaskRangeBits) +{ + EXPECT_EQ(mask_range_bits(0, 0), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_range_bits(0, 1), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001); + EXPECT_EQ(mask_range_bits(0, 5), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111); + EXPECT_EQ(mask_range_bits(64, 0), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_range_bits(63, 1), + 0b1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_range_bits(59, 5), + 0b1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); + EXPECT_EQ(mask_range_bits(8, 3), + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0111'0000'0000); + EXPECT_EQ(mask_range_bits(0, 64), + 0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111); +} + +} // namespace blender::bits::tests diff --git a/source/blender/blenlib/tests/BLI_bit_span_test.cc b/source/blender/blenlib/tests/BLI_bit_span_test.cc new file mode 100644 index 00000000000..4ffaadd1ddb --- /dev/null +++ b/source/blender/blenlib/tests/BLI_bit_span_test.cc @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#include "BLI_bit_span.hh" + +#include "testing/testing.h" + +namespace blender::bits::tests { + +TEST(bit_span, DefaultConstructor) +{ + { + char buffer[sizeof(BitSpan)]; + memset(buffer, 0xff, sizeof(BitSpan)); + BitSpan &span = *new (buffer) BitSpan(); + EXPECT_TRUE(span.is_empty()); + EXPECT_EQ(span.size(), 0); + } + { + char buffer[sizeof(MutableBitSpan)]; + memset(buffer, 0xff, sizeof(MutableBitSpan)); + MutableBitSpan &span = *new (buffer) MutableBitSpan(); + EXPECT_TRUE(span.is_empty()); + EXPECT_EQ(span.size(), 0); + } +} + +TEST(bit_span, Iteration) +{ + uint64_t data = (1 << 2) | (1 << 3); + const BitSpan span(&data, 30); + EXPECT_EQ(span.size(), 30); + int index = 0; + for (const BitRef bit : span) { + EXPECT_EQ(bit.test(), ELEM(index, 2, 3)); + index++; + } +} + +TEST(bit_span, MutableIteration) +{ + uint64_t data = 0; + MutableBitSpan span(&data, 40); + EXPECT_EQ(span.size(), 40); + int index = 0; + for (MutableBitRef bit : span) { + bit.set(index % 4 == 0); + index++; + } + EXPECT_EQ(data, + 0b0000'0000'0000'0000'0000'0000'0001'0001'0001'0001'0001'0001'0001'0001'0001'0001); +} + +TEST(bit_span, SubscriptOperator) +{ + uint64_t data[2] = {0, 0}; + MutableBitSpan mutable_span(data, 128); + BitSpan span = mutable_span; + + EXPECT_EQ(mutable_span.data(), data); + EXPECT_EQ(mutable_span.bit_range(), IndexRange(128)); + EXPECT_EQ(span.data(), data); + EXPECT_EQ(span.bit_range(), IndexRange(128)); + + EXPECT_FALSE(mutable_span[5].test()); + EXPECT_FALSE(span[5].test()); + mutable_span[5].set(5); + EXPECT_TRUE(mutable_span[5].test()); + EXPECT_TRUE(span[5].test()); + + EXPECT_FALSE(mutable_span[120].test()); + EXPECT_FALSE(span[120].test()); + mutable_span[120].set(120); + EXPECT_TRUE(mutable_span[120].test()); + EXPECT_TRUE(span[120].test()); + + EXPECT_EQ(data[0], + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000); + EXPECT_EQ(data[1], + 0b0000'0001'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000); +} + +TEST(bit_span, RangeConstructor) +{ + uint64_t data = 0; + MutableBitSpan mutable_span(&data, IndexRange(4, 3)); + BitSpan span = mutable_span; + + EXPECT_FALSE(mutable_span[1].test()); + EXPECT_FALSE(span[1].test()); + mutable_span[0].set(true); + mutable_span[1].set(true); + mutable_span[2].set(true); + mutable_span[0].set(false); + mutable_span[2].set(false); + EXPECT_TRUE(mutable_span[1].test()); + EXPECT_TRUE(span[1].test()); + + EXPECT_EQ(data, + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0010'0000); +} + +TEST(bit_span, Set) +{ + uint64_t data = 0; + MutableBitSpan(&data, 64).set_all(true); + EXPECT_EQ(data, uint64_t(-1)); + MutableBitSpan(&data, 64).set_all(false); + EXPECT_EQ(data, uint64_t(0)); + + MutableBitSpan(&data, IndexRange(4, 8)).set_all(true); + EXPECT_EQ(data, + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'1111'0000); + MutableBitSpan(&data, IndexRange(8, 30)).set_all(false); + + EXPECT_EQ(data, + 0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'1111'0000); +} + +TEST(bit_span, SetSliced) +{ + std::array data; + memset(data.data(), 0, sizeof(data)); + MutableBitSpan span{data.data(), 640}; + span.slice(IndexRange(5, 500)).set_all(true); + + for (const int64_t i : IndexRange(640)) { + EXPECT_EQ(span[i], i >= 5 && i < 505); + } + + span.slice(IndexRange(10, 190)).set_all(false); + + for (const int64_t i : IndexRange(640)) { + EXPECT_EQ(span[i], (i >= 5 && i < 10) || (i >= 200 && i < 505)); + } +} + +} // namespace blender::bits::tests diff --git a/source/blender/blenlib/tests/BLI_bit_vector_test.cc b/source/blender/blenlib/tests/BLI_bit_vector_test.cc index 210f2be012d..a8e199f6f64 100644 --- a/source/blender/blenlib/tests/BLI_bit_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_bit_vector_test.cc @@ -6,7 +6,7 @@ #include "testing/testing.h" -namespace blender::tests { +namespace blender::bits::tests { TEST(bit_vector, DefaultConstructor) { @@ -183,4 +183,4 @@ TEST(bit_vector, AppendMany) EXPECT_TRUE(vec[5]); } -} // namespace blender::tests +} // namespace blender::bits::tests diff --git a/source/blender/blenlib/tests/BLI_fileops_test.cc b/source/blender/blenlib/tests/BLI_fileops_test.cc index 2a5ecb7d11d..bed75e332f7 100644 --- a/source/blender/blenlib/tests/BLI_fileops_test.cc +++ b/source/blender/blenlib/tests/BLI_fileops_test.cc @@ -1,11 +1,27 @@ /* SPDX-License-Identifier: Apache-2.0 */ - -#include "BLI_fileops.hh" - #include "testing/testing.h" +#include "BLI_fileops.hh" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_threads.h" + namespace blender::tests { +class ChangeWorkingDirectoryTest : public testing::Test { + public: + std::string test_temp_dir; + + void TearDown() override + { + if (!test_temp_dir.empty()) { + BLI_delete(test_temp_dir.c_str(), true, false); + } + + BLI_threadapi_exit(); + } +}; + TEST(fileops, fstream_open_string_filename) { const std::string test_files_dir = blender::tests::flags_test_asset_dir(); @@ -37,4 +53,50 @@ TEST(fileops, fstream_open_charptr_filename) /* Reading the file not tested here. That's deferred to `std::fstream` anyway. */ } +TEST_F(ChangeWorkingDirectoryTest, change_working_directory) +{ + /* Must use because BLI_change_working_dir() checks that we are on the main thread. */ + BLI_threadapi_init(); + + char original_wd[FILE_MAX]; + if (!BLI_current_working_dir(original_wd, FILE_MAX)) { + FAIL() << "unable to get the current working directory"; + } + + std::string temp_file_name(std::tmpnam(nullptr)); + test_temp_dir = temp_file_name + "_новый"; + + if (BLI_exists(test_temp_dir.c_str())) { + BLI_delete(test_temp_dir.c_str(), true, false); + } + + ASSERT_FALSE(BLI_change_working_dir(test_temp_dir.c_str())) + << "changing directory to a non-existent directory is expected to fail."; + + ASSERT_TRUE(BLI_dir_create_recursive(test_temp_dir.c_str())) + << "temporary directory should have been created successfully."; + + ASSERT_TRUE(BLI_change_working_dir(test_temp_dir.c_str())) + << "temporary directory should succeed changing directory."; + + char cwd[FILE_MAX]; + if (!BLI_current_working_dir(cwd, FILE_MAX)) { + FAIL() << "unable to get the current working directory"; + } + +#ifdef __APPLE__ + /* The name returned by std::tmpnam is fine but the Apple OS method + * picks the true var folder, not the alias, meaning the below + * comparison always fails unless we prepend with the correct value. */ + test_temp_dir = "/private" + test_temp_dir; +#endif // #ifdef __APPLE__ + + ASSERT_EQ(BLI_path_cmp_normalized(cwd, test_temp_dir.c_str()), 0) + << "the path of the current working directory should equal the path of the temporary " + "directory that was created."; + + ASSERT_TRUE(BLI_change_working_dir(original_wd)) + << "changing directory back to the original working directory should succeed."; +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc index 8ec7ad85d9c..b76f597ded6 100644 --- a/source/blender/blenlib/tests/BLI_index_range_test.cc +++ b/source/blender/blenlib/tests/BLI_index_range_test.cc @@ -290,6 +290,24 @@ TEST(index_range, SplitByAlignment) EXPECT_EQ(ranges.aligned, IndexRange()); EXPECT_EQ(ranges.suffix, IndexRange()); } + { + AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64), 64); + EXPECT_EQ(ranges.prefix, IndexRange()); + EXPECT_EQ(ranges.aligned, IndexRange(64)); + EXPECT_EQ(ranges.suffix, IndexRange()); + } + { + AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64, 64), 64); + EXPECT_EQ(ranges.prefix, IndexRange()); + EXPECT_EQ(ranges.aligned, IndexRange(64, 64)); + EXPECT_EQ(ranges.suffix, IndexRange()); + } + { + AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(4, 8), 64); + EXPECT_EQ(ranges.prefix, IndexRange(4, 8)); + EXPECT_EQ(ranges.aligned, IndexRange()); + EXPECT_EQ(ranges.suffix, IndexRange()); + } } } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_math_matrix_test.cc b/source/blender/blenlib/tests/BLI_math_matrix_test.cc index 4a9f7936444..e04625c4163 100644 --- a/source/blender/blenlib/tests/BLI_math_matrix_test.cc +++ b/source/blender/blenlib/tests/BLI_math_matrix_test.cc @@ -100,6 +100,40 @@ TEST(math_matrix, interp_m3_m3m3_singularity) EXPECT_M3_NEAR(result, expect, 1e-5); } +TEST(math_matrix, mul_m3_series) +{ + float matrix[3][3] = { + {2.0f, 0.0f, 0.0f}, + {0.0f, 3.0f, 0.0f}, + {0.0f, 0.0f, 5.0f}, + }; + mul_m3_series(matrix, matrix, matrix, matrix); + float expect[3][3] = { + {8.0f, 0.0f, 0.0f}, + {0.0f, 27.0f, 0.0f}, + {0.0f, 0.0f, 125.0f}, + }; + EXPECT_M3_NEAR(matrix, expect, 1e-5); +} + +TEST(math_matrix, mul_m4_series) +{ + float matrix[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 3.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 5.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 7.0f}, + }; + mul_m4_series(matrix, matrix, matrix, matrix); + float expect[4][4] = { + {8.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 27.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 125.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 343.0f}, + }; + EXPECT_M4_NEAR(matrix, expect, 1e-5); +} + namespace blender::tests { using namespace blender::math; diff --git a/source/blender/blenloader/intern/versioning_290.cc b/source/blender/blenloader/intern/versioning_290.cc index b90e7529bbc..580a1437c16 100644 --- a/source/blender/blenloader/intern/versioning_290.cc +++ b/source/blender/blenloader/intern/versioning_290.cc @@ -816,20 +816,21 @@ void blo_do_versions_290(FileData *fd, Library * /*lib*/, Main *bmain) if (MAIN_VERSION_ATLEAST(bmain, 290, 2) && MAIN_VERSION_OLDER(bmain, 291, 1)) { /* In this range, the extrude manifold could generate meshes with degenerated face. */ LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) { - for (const MPoly *mp = BKE_mesh_polys(me), *mp_end = mp + me->totpoly; mp < mp_end; mp++) { - if (mp->totloop == 2) { + blender::MutableSpan polys = me->polys_for_write(); + for (const int i : polys.index_range()) { + if (polys[i].totloop == 2) { bool changed; BKE_mesh_validate_arrays( me, BKE_mesh_vert_positions_for_write(me), me->totvert, - BKE_mesh_edges_for_write(me), + me->edges_for_write().data(), me->totedge, (MFace *)CustomData_get_layer_for_write(&me->fdata, CD_MFACE, me->totface), me->totface, BKE_mesh_loops_for_write(me), - me->totloop, - BKE_mesh_polys_for_write(me), + polys.size(), + polys.data(), me->totpoly, BKE_mesh_deform_verts_for_write(me), false, diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 4a1c14bd430..59e4be70989 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -77,6 +77,7 @@ #include "SEQ_channels.h" #include "SEQ_iterator.h" +#include "SEQ_retiming.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" @@ -685,6 +686,25 @@ 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(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(scene, 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 = 0.0f; + + return true; +} + static void version_geometry_nodes_replace_transfer_attribute_node(bNodeTree *ntree) { using namespace blender; @@ -1205,6 +1225,16 @@ void do_versions_after_linking_300(Main *bmain, ReportList * /*reports*/) */ { /* Keep this block, even when empty. */ + + 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); + } } } diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h index f788da8d429..1ee40b1d890 100644 --- a/source/blender/blentranslation/BLT_translation.h +++ b/source/blender/blentranslation/BLT_translation.h @@ -126,7 +126,7 @@ const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid #define BLT_I18NCONTEXT_EDITOR_VIEW3D "View3D" #define BLT_I18NCONTEXT_EDITOR_FILEBROWSER "File browser" - /* Generic contexts. */ +/* Generic contexts. */ #define BLT_I18NCONTEXT_VIRTUAL_REALITY "Virtual reality" #define BLT_I18NCONTEXT_CONSTRAINT "Constraint" @@ -194,7 +194,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_VIEW3D, "editor_view3d"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_FILEBROWSER, "editor_filebrowser"), \ - BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_VIRTUAL_REALITY, "virtual_reality"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_VIRTUAL_REALITY, "virtual_reality"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_CONSTRAINT, "constraint"), \ { \ NULL, NULL, NULL \ diff --git a/source/blender/bmesh/intern/bmesh_iterators.cc b/source/blender/bmesh/intern/bmesh_iterators.cc index d712fe8c731..c27d02b9a04 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.cc +++ b/source/blender/bmesh/intern/bmesh_iterators.cc @@ -211,7 +211,7 @@ void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], int BM_iter_mesh_bitmap_from_filter(const char itype, BMesh *bm, - blender::BitVector<> &bitmap, + blender::MutableBitSpan bitmap, bool (*test_fn)(BMElem *, void *user_data), void *user_data) { @@ -234,7 +234,7 @@ int BM_iter_mesh_bitmap_from_filter(const char itype, } int BM_iter_mesh_bitmap_from_filter_tessface(BMesh *bm, - blender::BitVector<> &bitmap, + blender::MutableBitSpan bitmap, bool (*test_fn)(BMFace *, void *user_data), void *user_data) { diff --git a/source/blender/bmesh/intern/bmesh_iterators.h b/source/blender/bmesh/intern/bmesh_iterators.h index d84f163f978..a095e2831bf 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.h +++ b/source/blender/bmesh/intern/bmesh_iterators.h @@ -21,7 +21,7 @@ #include "BLI_mempool.h" #ifdef __cplusplus -# include "BLI_bit_vector.hh" +# include "BLI_bit_span.hh" #endif #ifdef __cplusplus @@ -228,14 +228,14 @@ void *BMO_iter_as_arrayN(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], int BM_iter_mesh_bitmap_from_filter(char itype, BMesh *bm, - blender::BitVector<> &bitmap, + blender::MutableBitSpan bitmap, bool (*test_fn)(BMElem *, void *user_data), void *user_data); /** * Needed when we want to check faces, but return a loop aligned array. */ int BM_iter_mesh_bitmap_from_filter_tessface(BMesh *bm, - blender::BitVector<> &bitmap, + blender::MutableBitSpan bitmap, bool (*test_fn)(BMFace *, void *user_data), void *user_data); diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index ea193cc8d85..1781a99c0ab 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -1191,7 +1191,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh need_edgesel |= BM_ELEM_CD_GET_BOOL(l, edgesel_offset); } } - if (pin_layer_index) { + if (pin_layer_index >= 0) { BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { need_pin |= BM_ELEM_CD_GET_BOOL(l, pin_offset); } diff --git a/source/blender/draw/engines/overlay/shaders/overlay_edit_mesh_vert_no_geom.glsl b/source/blender/draw/engines/overlay/shaders/overlay_edit_mesh_vert_no_geom.glsl index 504b6ea2f44..33217603b95 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_edit_mesh_vert_no_geom.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_edit_mesh_vert_no_geom.glsl @@ -59,9 +59,9 @@ void main() * IF PrimType == LineList: base_vertex_id = quad_id*2 * IF PrimType == LineStrip: base_vertex_id = quad_id * - * Note: This is currently used as LineList. + * NOTE: This is currently used as LineList. * - * Note: Primitive Restart Will not work with this setup as-is. We should avoid using + * NOTE: Primitive Restart Will not work with this setup as-is. We should avoid using * input primitive types which use restart indices. */ int base_vertex_id = quad_id * 2; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index 0937e9feb54..b83297307ae 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -546,9 +546,9 @@ MeshRenderData *mesh_render_data_create(Object *object, mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); mr->vert_positions = mr->me->vert_positions().data(); - mr->medge = BKE_mesh_edges(mr->me); - mr->mpoly = BKE_mesh_polys(mr->me); - mr->mloop = BKE_mesh_loops(mr->me); + mr->medge = mr->me->edges().data(); + mr->mpoly = mr->me->polys().data(); + mr->mloop = mr->me->loops().data(); mr->v_origindex = static_cast(CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX)); mr->e_origindex = static_cast(CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX)); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 0fe5eba26f4..de8df455355 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -16,8 +16,8 @@ #include "BKE_scene.h" #include "BKE_subdiv.h" #include "BKE_subdiv_eval.h" -#include "BKE_subdiv_foreach.h" -#include "BKE_subdiv_mesh.h" +#include "BKE_subdiv_foreach.hh" +#include "BKE_subdiv_mesh.hh" #include "BKE_subdiv_modifier.h" #include "BLI_linklist.h" diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc index 233241ad776..2dcbc6d0dc6 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc @@ -557,7 +557,7 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, const bool mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false; if (mr->use_subsurf_fdots) { - const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags; + const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags; const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc index bd04f6dbb03..716acd6e2fd 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc @@ -46,7 +46,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, GPUIndexBufBuilder *elb = static_cast(_userdata); if (mr->use_subsurf_fdots) { - const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags; + const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags; const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc index 4360fd16b9d..51335d3cbb4 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc @@ -102,7 +102,7 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_EdgeFac_Data *data = static_cast(_data); - const BitVector<> &optimal_display_edges = mr->me->runtime->subsurf_optimal_display_edges; + const BitSpan optimal_display_edges = mr->me->runtime->subsurf_optimal_display_edges; const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc index b9f2917d0fe..084f1cf25c6 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc @@ -76,7 +76,7 @@ static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, zero_v3(co); const MLoop *mloop = mr->mloop; - const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags; + const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc index 1ed6dc26b6f..cc127fa9899 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc @@ -74,7 +74,7 @@ static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_FdotUV_Data *data = static_cast(_data); - const BitVector<> &facedot_tags = mr->me->runtime->subsurf_face_dot_tags; + const BitSpan facedot_tags = mr->me->runtime->subsurf_face_dot_tags; const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 298b48a4a90..2a761166b27 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -47,6 +47,7 @@ #include "ED_anim_api.h" #include "ED_armature.h" #include "ED_keyframes_edit.h" /* XXX move the select modes out of there! */ +#include "ED_markers.h" #include "ED_object.h" #include "ED_screen.h" #include "ED_select_utils.h" @@ -3638,6 +3639,305 @@ static void ANIM_OT_channel_select_keys(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name View Channel Operator + * \{ */ + +static void get_normalized_fcurve_bounds(FCurve *fcu, + bAnimContext *ac, + const bAnimListElem *ale, + const bool include_handles, + const float range[2], + rctf *r_bounds) +{ + const bool fcu_selection_only = false; + BKE_fcurve_calc_bounds(fcu, + &r_bounds->xmin, + &r_bounds->xmax, + &r_bounds->ymin, + &r_bounds->ymax, + fcu_selection_only, + include_handles, + range); + const short mapping_flag = ANIM_get_normalization_flags(ac); + + float offset; + const float unit_fac = ANIM_unit_mapping_get_factor( + ac->scene, ale->id, fcu, mapping_flag, &offset); + + r_bounds->ymin = (r_bounds->ymin + offset) * unit_fac; + r_bounds->ymax = (r_bounds->ymax + offset) * unit_fac; + + const float min_height = 0.01f; + const float height = BLI_rctf_size_y(r_bounds); + if (height < min_height) { + r_bounds->ymin -= (min_height - height) / 2; + r_bounds->ymax += (min_height - height) / 2; + } +} + +static void get_gpencil_bounds(bGPDlayer *gpl, const float range[2], rctf *r_bounds) +{ + bool found_start = false; + int start_frame = 0; + int end_frame = 1; + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + if (gpf->framenum < range[0]) { + continue; + } + if (gpf->framenum > range[1]) { + break; + } + if (!found_start) { + start_frame = gpf->framenum; + found_start = true; + } + end_frame = gpf->framenum; + } + r_bounds->xmin = start_frame; + r_bounds->xmax = end_frame; + r_bounds->ymin = 0; + r_bounds->ymax = 1; +} + +static bool get_channel_bounds(bAnimContext *ac, + bAnimListElem *ale, + const float range[2], + const bool include_handles, + rctf *r_bounds) +{ + bool found_bounds = false; + switch (ale->datatype) { + case ALE_GPFRAME: { + bGPDlayer *gpl = (bGPDlayer *)ale->data; + get_gpencil_bounds(gpl, range, r_bounds); + found_bounds = true; + break; + } + case ALE_FCURVE: { + FCurve *fcu = (FCurve *)ale->key_data; + get_normalized_fcurve_bounds(fcu, ac, ale, include_handles, range, r_bounds); + found_bounds = true; + break; + } + } + return found_bounds; +} + +static void get_view_range(Scene *scene, const bool use_preview_range, float r_range[2]) +{ + if (use_preview_range && scene->r.flag & SCER_PRV_RANGE) { + r_range[0] = scene->r.psfra; + r_range[1] = scene->r.pefra; + } + else { + r_range[0] = -FLT_MAX; + r_range[1] = FLT_MAX; + } +} + +/* Pad the given rctf with regions that could block the view. + * For example Markers and Time Scrubbing. */ +static void add_region_padding(bContext *C, bAnimContext *ac, rctf *bounds) +{ + BLI_rctf_scale(bounds, 1.1f); + + const float pad_top = UI_TIME_SCRUB_MARGIN_Y; + const float pad_bottom = BLI_listbase_is_empty(ED_context_get_markers(C)) ? + V2D_SCROLL_HANDLE_HEIGHT : + UI_MARKER_MARGIN_Y; + BLI_rctf_pad_y(bounds, ac->region->winy, pad_bottom, pad_top); +} + +static ARegion *get_window_region(bAnimContext *ac) +{ + LISTBASE_FOREACH (ARegion *, region, &ac->area->regionbase) { + if (region->regiontype == RGN_TYPE_WINDOW) { + return region; + } + } + return NULL; +} + +static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + /* Get editor data. */ + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + ARegion *window_region = get_window_region(&ac); + + if (!window_region) { + return OPERATOR_CANCELLED; + } + + ListBase anim_data = {NULL, NULL}; + const int filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_DATA_VISIBLE | + ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + size_t anim_data_length = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + if (anim_data_length == 0) { + WM_report(RPT_WARNING, "No channels to operate on"); + return OPERATOR_CANCELLED; + } + + float range[2]; + const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range"); + get_view_range(ac.scene, use_preview_range, range); + + rctf bounds = {.xmin = FLT_MAX, .xmax = -FLT_MAX, .ymin = FLT_MAX, .ymax = -FLT_MAX}; + + bAnimListElem *ale; + const bool include_handles = RNA_boolean_get(op->ptr, "include_handles"); + + bool valid_bounds = false; + for (ale = anim_data.first; ale; ale = ale->next) { + rctf channel_bounds; + const bool found_bounds = get_channel_bounds( + &ac, ale, range, include_handles, &channel_bounds); + if (found_bounds) { + BLI_rctf_union(&bounds, &channel_bounds); + valid_bounds = true; + } + } + + if (!valid_bounds) { + ANIM_animdata_freelist(&anim_data); + return OPERATOR_CANCELLED; + } + + add_region_padding(C, &ac, &bounds); + + if (ac.spacetype == SPACE_ACTION) { + bounds.ymin = window_region->v2d.cur.ymin; + bounds.ymax = window_region->v2d.cur.ymax; + } + + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + UI_view2d_smooth_view(C, window_region, &bounds, smooth_viewtx); + + ANIM_animdata_freelist(&anim_data); + + return OPERATOR_FINISHED; +} + +static bool channel_view_poll(bContext *C) +{ + return ED_operator_action_active(C) || ED_operator_graphedit_active(C); +} + +static void ANIM_OT_channels_view_selected(wmOperatorType *ot) +{ + /* Identifiers */ + ot->name = "Frame Selected Channels"; + ot->idname = "ANIM_OT_channels_view_selected"; + ot->description = "Reset viewable area to show the selected channels"; + + /* API callbacks */ + ot->exec = graphkeys_view_selected_channels_exec; + ot->poll = channel_view_poll; + + ot->flag = 0; + + ot->prop = RNA_def_boolean(ot->srna, + "include_handles", + true, + "Include Handles", + "Include handles of keyframes when calculating extents"); + + ot->prop = RNA_def_boolean(ot->srna, + "use_preview_range", + true, + "Use Preview Range", + "Ignore frames outside of the preview range"); +} + +static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + bAnimContext ac; + + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + ARegion *window_region = get_window_region(&ac); + + if (!window_region) { + return OPERATOR_CANCELLED; + } + + ListBase anim_data = {NULL, NULL}; + const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS | + ANIMFILTER_LIST_CHANNELS); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + bAnimListElem *ale; + const int channel_index = animchannels_channel_get(&ac, event->mval); + ale = BLI_findlink(&anim_data, channel_index); + if (ale == NULL) { + ANIM_animdata_freelist(&anim_data); + return OPERATOR_CANCELLED; + } + + float range[2]; + const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range"); + get_view_range(ac.scene, use_preview_range, range); + + rctf bounds; + const bool include_handles = RNA_boolean_get(op->ptr, "include_handles"); + const bool found_bounds = get_channel_bounds(&ac, ale, range, include_handles, &bounds); + + if (!found_bounds) { + ANIM_animdata_freelist(&anim_data); + return OPERATOR_CANCELLED; + } + + add_region_padding(C, &ac, &bounds); + + if (ac.spacetype == SPACE_ACTION) { + bounds.ymin = window_region->v2d.cur.ymin; + bounds.ymax = window_region->v2d.cur.ymax; + } + + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + UI_view2d_smooth_view(C, window_region, &bounds, smooth_viewtx); + + ANIM_animdata_freelist(&anim_data); + + return OPERATOR_FINISHED; +} + +static void ANIM_OT_channel_view_pick(wmOperatorType *ot) +{ + /* Identifiers */ + ot->name = "Frame Channel Under Cursor"; + ot->idname = "ANIM_OT_channel_view_pick"; + ot->description = "Reset viewable area to show the channel under the cursor"; + + /* API callbacks */ + ot->invoke = graphkeys_channel_view_pick_invoke; + ot->poll = channel_view_poll; + + ot->flag = 0; + + ot->prop = RNA_def_boolean(ot->srna, + "include_handles", + true, + "Include Handles", + "Include handles of keyframes when calculating extents"); + + ot->prop = RNA_def_boolean(ot->srna, + "use_preview_range", + true, + "Use Preview Range", + "Ignore frames outside of the preview range"); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Operator Registration * \{ */ @@ -3657,6 +3957,9 @@ void ED_operatortypes_animchannels(void) WM_operatortype_append(ANIM_OT_channels_setting_disable); WM_operatortype_append(ANIM_OT_channels_setting_toggle); + WM_operatortype_append(ANIM_OT_channel_view_pick); + WM_operatortype_append(ANIM_OT_channels_view_selected); + WM_operatortype_append(ANIM_OT_channels_delete); /* XXX does this need to be a separate operator? */ diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index c435983be19..b2b44baf3f2 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -20,12 +20,14 @@ #include "DNA_anim_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BKE_action.h" #include "BKE_curve.h" #include "BKE_fcurve.h" #include "BKE_main.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "RNA_access.h" #include "RNA_enum_types.h" @@ -1074,7 +1076,7 @@ static void do_curve_mirror_flippping(tAnimCopybufItem *aci, BezTriple *bezt) /* helper for paste_animedit_keys() - performs the actual pasting */ static void paste_animedit_keys_fcurve( - FCurve *fcu, tAnimCopybufItem *aci, float offset, const eKeyMergeMode merge_mode, bool flip) + FCurve *fcu, tAnimCopybufItem *aci, float offset[2], const eKeyMergeMode merge_mode, bool flip) { BezTriple *bezt; int i; @@ -1101,12 +1103,12 @@ static void paste_animedit_keys_fcurve( float f_max; if (merge_mode == KEYFRAME_PASTE_MERGE_OVER_RANGE) { - f_min = aci->bezt[0].vec[1][0] + offset; - f_max = aci->bezt[aci->totvert - 1].vec[1][0] + offset; + f_min = aci->bezt[0].vec[1][0] + offset[0]; + f_max = aci->bezt[aci->totvert - 1].vec[1][0] + offset[0]; } else { /* Entire Range */ - f_min = animcopy_firstframe + offset; - f_max = animcopy_lastframe + offset; + f_min = animcopy_firstframe + offset[0]; + f_max = animcopy_lastframe + offset[0]; } /* remove keys in range */ @@ -1132,9 +1134,9 @@ static void paste_animedit_keys_fcurve( do_curve_mirror_flippping(aci, bezt); } - bezt->vec[0][0] += offset; - bezt->vec[1][0] += offset; - bezt->vec[2][0] += offset; + add_v2_v2(bezt->vec[0], offset); + add_v2_v2(bezt->vec[1], offset); + add_v2_v2(bezt->vec[2], offset); /* insert the keyframe * NOTE: we do not want to inherit handles from existing keyframes in this case! @@ -1143,9 +1145,9 @@ static void paste_animedit_keys_fcurve( insert_bezt_fcurve(fcu, bezt, INSERTKEY_OVERWRITE_FULL); /* un-apply offset from src beztriple after copying */ - bezt->vec[0][0] -= offset; - bezt->vec[1][0] -= offset; - bezt->vec[2][0] -= offset; + sub_v2_v2(bezt->vec[0], offset); + sub_v2_v2(bezt->vec[1], offset); + sub_v2_v2(bezt->vec[2], offset); if (flip) { do_curve_mirror_flippping(aci, bezt); @@ -1172,6 +1174,35 @@ const EnumPropertyItem rna_enum_keyframe_paste_offset_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_keyframe_paste_offset_value[] = { + {KEYFRAME_PASTE_VALUE_OFFSET_LEFT_KEY, + "LEFT_KEY", + 0, + "Left Key", + "Paste keys with the first key matching the key left of the cursor"}, + {KEYFRAME_PASTE_VALUE_OFFSET_RIGHT_KEY, + "RIGHT_KEY", + 0, + "Right Key", + "Paste keys with the last key matching the key right of the cursor"}, + {KEYFRAME_PASTE_VALUE_OFFSET_CFRA, + "CURRENT_FRAME", + 0, + "Current Frame Value", + "Paste keys relative to the value of the curve under the cursor"}, + {KEYFRAME_PASTE_VALUE_OFFSET_CURSOR, + "CURSOR_VALUE", + 0, + "Cursor Value", + "Paste keys relative to the Y-Position of the cursor"}, + {KEYFRAME_PASTE_VALUE_OFFSET_NONE, + "NONE", + 0, + "No Offset", + "Paste keys with the same value as they were copied"}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_keyframe_paste_merge_items[] = { {KEYFRAME_PASTE_MERGE_MIX, "MIX", 0, "Mix", "Overlay existing with new keys"}, {KEYFRAME_PASTE_MERGE_OVER, "OVER_ALL", 0, "Overwrite All", "Replace all keys"}, @@ -1188,9 +1219,56 @@ const EnumPropertyItem rna_enum_keyframe_paste_merge_items[] = { {0, NULL, 0, NULL, NULL}, }; +static float paste_get_y_offset(bAnimContext *ac, + tAnimCopybufItem *aci, + bAnimListElem *ale, + const eKeyPasteValueOffset value_offset_mode) +{ + FCurve *fcu = (FCurve *)ale->data; + const float cfra = BKE_scene_frame_get(ac->scene); + + switch (value_offset_mode) { + case KEYFRAME_PASTE_VALUE_OFFSET_CURSOR: { + SpaceGraph *sipo = (SpaceGraph *)ac->sl; + const float offset = sipo->cursorVal - aci->bezt[0].vec[1][1]; + return offset; + } + + case KEYFRAME_PASTE_VALUE_OFFSET_CFRA: { + const float cfra_y = evaluate_fcurve(fcu, cfra); + const float offset = cfra_y - aci->bezt[0].vec[1][1]; + return offset; + } + + case KEYFRAME_PASTE_VALUE_OFFSET_LEFT_KEY: { + bool replace; + const int fcu_index = BKE_fcurve_bezt_binarysearch_index( + fcu->bezt, cfra, fcu->totvert, &replace); + BezTriple left_key = fcu->bezt[max_ii(fcu_index - 1, 0)]; + const float offset = left_key.vec[1][1] - aci->bezt[0].vec[1][1]; + return offset; + } + + case KEYFRAME_PASTE_VALUE_OFFSET_RIGHT_KEY: { + bool replace; + const int fcu_index = BKE_fcurve_bezt_binarysearch_index( + fcu->bezt, cfra, fcu->totvert, &replace); + BezTriple right_key = fcu->bezt[min_ii(fcu_index, fcu->totvert - 1)]; + const float offset = right_key.vec[1][1] - aci->bezt[aci->totvert - 1].vec[1][1]; + return offset; + } + + case KEYFRAME_PASTE_VALUE_OFFSET_NONE: + break; + } + + return 0.0f; +} + eKeyPasteError paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, const eKeyPasteOffset offset_mode, + const eKeyPasteValueOffset value_offset_mode, const eKeyMergeMode merge_mode, bool flip) { @@ -1201,7 +1279,7 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac, const bool from_single = BLI_listbase_is_single(&animcopybuf); const bool to_simple = BLI_listbase_is_single(anim_data); - float offset = 0.0f; + float offset[2]; int pass; /* check if buffer is empty */ @@ -1216,16 +1294,16 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac, /* methods of offset */ switch (offset_mode) { case KEYFRAME_PASTE_OFFSET_CFRA_START: - offset = (float)(scene->r.cfra - animcopy_firstframe); + offset[0] = (float)(scene->r.cfra - animcopy_firstframe); break; case KEYFRAME_PASTE_OFFSET_CFRA_END: - offset = (float)(scene->r.cfra - animcopy_lastframe); + offset[0] = (float)(scene->r.cfra - animcopy_lastframe); break; case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE: - offset = (float)(scene->r.cfra - animcopy_cfra); + offset[0] = (float)(scene->r.cfra - animcopy_cfra); break; case KEYFRAME_PASTE_OFFSET_NONE: - offset = 0.0f; + offset[0] = 0.0f; break; } @@ -1238,6 +1316,7 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac, fcu = (FCurve *)ale->data; /* destination F-Curve */ aci = animcopybuf.first; + offset[1] = paste_get_y_offset(ac, aci, ale, value_offset_mode); paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, false); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -1282,6 +1361,7 @@ eKeyPasteError paste_animedit_keys(bAnimContext *ac, if (aci) { totmatch++; + offset[1] = paste_get_y_offset(ac, aci, ale, value_offset_mode); if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, flip); diff --git a/source/blender/editors/armature/meshlaplacian.cc b/source/blender/editors/armature/meshlaplacian.cc index 7cd25577ad2..25af4ea10ee 100644 --- a/source/blender/editors/armature/meshlaplacian.cc +++ b/source/blender/editors/armature/meshlaplacian.cc @@ -74,7 +74,7 @@ struct LaplacianSystem { struct HeatWeighting { const MLoopTri *mlooptri; - const MLoop *mloop; /* needed to find vertices by index */ + blender::Span loops; /* needed to find vertices by index */ int verts_num; int tris_num; float (*verts)[3]; /* vertex coordinates */ @@ -205,7 +205,7 @@ static LaplacianSystem *laplacian_system_construct_begin(int verts_num, int face { LaplacianSystem *sys; - sys = MEM_cnew(__func__); + sys = MEM_new(__func__); sys->verts = static_cast( MEM_callocN(sizeof(float *) * verts_num, "LaplacianSystemVerts")); @@ -323,7 +323,7 @@ static void laplacian_system_delete(LaplacianSystem *sys) } EIG_linear_solver_delete(sys->context); - MEM_freeN(sys); + MEM_delete(sys); } void laplacian_begin_solve(LaplacianSystem *sys, int index) @@ -382,14 +382,14 @@ static void bvh_callback(void *userdata, int index, const BVHTreeRay *ray, BVHTr { BVHCallbackUserData *data = (struct BVHCallbackUserData *)userdata; const MLoopTri *lt = &data->sys->heat.mlooptri[index]; - const MLoop *mloop = data->sys->heat.mloop; + const blender::Span loops = data->sys->heat.loops; float(*verts)[3] = data->sys->heat.verts; const float *vtri_co[3]; float dist_test; - vtri_co[0] = verts[mloop[lt->tri[0]].v]; - vtri_co[1] = verts[mloop[lt->tri[1]].v]; - vtri_co[2] = verts[mloop[lt->tri[2]].v]; + vtri_co[0] = verts[loops[lt->tri[0]].v]; + vtri_co[1] = verts[loops[lt->tri[1]].v]; + vtri_co[2] = verts[loops[lt->tri[2]].v]; #ifdef USE_KDOPBVH_WATERTIGHT if (isect_ray_tri_watertight_v3( @@ -414,7 +414,7 @@ static void bvh_callback(void *userdata, int index, const BVHTreeRay *ray, BVHTr static void heat_ray_tree_create(LaplacianSystem *sys) { const MLoopTri *looptri = sys->heat.mlooptri; - const MLoop *mloop = sys->heat.mloop; + const blender::Span loops = sys->heat.loops; float(*verts)[3] = sys->heat.verts; int tris_num = sys->heat.tris_num; int verts_num = sys->heat.verts_num; @@ -429,9 +429,9 @@ static void heat_ray_tree_create(LaplacianSystem *sys) float bb[6]; int vtri[3]; - vtri[0] = mloop[lt->tri[0]].v; - vtri[1] = mloop[lt->tri[1]].v; - vtri[2] = mloop[lt->tri[2]].v; + vtri[0] = loops[lt->tri[0]].v; + vtri[1] = loops[lt->tri[1]].v; + vtri[2] = loops[lt->tri[2]].v; INIT_MINMAX(bb, bb + 3); minmax_v3v3_v3(bb, bb + 3, verts[vtri[0]]); @@ -581,7 +581,7 @@ static void heat_calc_vnormals(LaplacianSystem *sys) static void heat_laplacian_create(LaplacianSystem *sys) { const MLoopTri *mlooptri = sys->heat.mlooptri, *lt; - const MLoop *mloop = sys->heat.mloop; + const blender::Span loops = sys->heat.loops; int tris_num = sys->heat.tris_num; int verts_num = sys->heat.verts_num; int a; @@ -598,9 +598,9 @@ static void heat_laplacian_create(LaplacianSystem *sys) for (a = 0, lt = mlooptri; a < tris_num; a++, lt++) { int vtri[3]; - vtri[0] = mloop[lt->tri[0]].v; - vtri[1] = mloop[lt->tri[1]].v; - vtri[2] = mloop[lt->tri[2]].v; + vtri[0] = loops[lt->tri[0]].v; + vtri[1] = loops[lt->tri[1]].v; + vtri[2] = loops[lt->tri[2]].v; laplacian_add_triangle(sys, UNPACK3(vtri)); } @@ -651,16 +651,14 @@ void heat_bone_weighting(Object *ob, { LaplacianSystem *sys; MLoopTri *mlooptri; - const MPoly *mp; - const MLoop *ml; float solution, weight; int *vertsflipped = nullptr, *mask = nullptr; int a, tris_num, j, bbone, firstsegment, lastsegment; bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; const float(*vert_positions)[3] = BKE_mesh_vert_positions(me); - const MPoly *polys = BKE_mesh_polys(me); - const MLoop *loops = BKE_mesh_loops(me); + const blender::Span polys = me->polys(); + const blender::Span loops = me->loops(); bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; @@ -678,9 +676,9 @@ void heat_bone_weighting(Object *ob, const bool *select_vert = (const bool *)CustomData_get_layer_named( &me->vdata, CD_PROP_BOOL, ".select_vert"); if (select_vert) { - for (a = 0, mp = polys; a < me->totpoly; mp++, a++) { - for (j = 0, ml = loops + mp->loopstart; j < mp->totloop; j++, ml++) { - mask[ml->v] = select_vert[ml->v]; + for (const int i : polys.index_range()) { + for (const MLoop &loop : loops.slice(polys[i].loopstart, polys[i].totloop)) { + mask[loop.v] = select_vert[loop.v]; } } } @@ -689,10 +687,10 @@ void heat_bone_weighting(Object *ob, const bool *select_poly = (const bool *)CustomData_get_layer_named( &me->pdata, CD_PROP_BOOL, ".select_poly"); if (select_poly) { - for (a = 0, mp = polys; a < me->totpoly; mp++, a++) { - if (select_poly[a]) { - for (j = 0, ml = loops + mp->loopstart; j < mp->totloop; j++, ml++) { - mask[ml->v] = 1; + for (const int i : polys.index_range()) { + if (select_poly[i]) { + for (const MLoop &loop : loops.slice(polys[i].loopstart, polys[i].totloop)) { + mask[loop.v] = 1; } } } @@ -707,10 +705,11 @@ void heat_bone_weighting(Object *ob, mlooptri = static_cast( MEM_mallocN(sizeof(*sys->heat.mlooptri) * sys->heat.tris_num, __func__)); - BKE_mesh_recalc_looptri(loops, polys, vert_positions, me->totloop, me->totpoly, mlooptri); + BKE_mesh_recalc_looptri( + loops.data(), polys.data(), vert_positions, me->totloop, me->totpoly, mlooptri); sys->heat.mlooptri = mlooptri; - sys->heat.mloop = loops; + sys->heat.loops = loops; sys->heat.verts_num = me->totvert; sys->heat.verts = verts; sys->heat.root = root; @@ -927,8 +926,8 @@ typedef struct MeshDeformBind { /* avoid DM function calls during intersections */ struct { - const MPoly *mpoly; - const MLoop *mloop; + blender::Span polys; + blender::Span loops; const MLoopTri *looptri; const float (*poly_nors)[3]; } cagemesh_cache; @@ -959,7 +958,7 @@ static void harmonic_ray_callback(void *userdata, { MeshRayCallbackData *data = static_cast(userdata); MeshDeformBind *mdb = data->mdb; - const MLoop *mloop = mdb->cagemesh_cache.mloop; + const blender::Span loops = mdb->cagemesh_cache.loops; const MLoopTri *looptri = mdb->cagemesh_cache.looptri, *lt; const float(*poly_nors)[3] = mdb->cagemesh_cache.poly_nors; MeshDeformIsect *isec = data->isec; @@ -968,9 +967,9 @@ static void harmonic_ray_callback(void *userdata, lt = &looptri[index]; - face[0] = mdb->cagecos[mloop[lt->tri[0]].v]; - face[1] = mdb->cagecos[mloop[lt->tri[1]].v]; - face[2] = mdb->cagecos[mloop[lt->tri[2]].v]; + face[0] = mdb->cagecos[loops[lt->tri[0]].v]; + face[1] = mdb->cagecos[loops[lt->tri[1]].v]; + face[2] = mdb->cagecos[loops[lt->tri[2]].v]; bool isect_ray_tri = isect_ray_tri_watertight_v3( ray->origin, ray->isect_precalc, UNPACK3(face), &dist, nullptr); @@ -1034,9 +1033,9 @@ static MDefBoundIsect *meshdeform_ray_tree_intersect(MeshDeformBind *mdb, harmonic_ray_callback, &data, BVH_RAYCAST_WATERTIGHT) != -1) { - const MLoop *mloop = mdb->cagemesh_cache.mloop; + const blender::Span loops = mdb->cagemesh_cache.loops; const MLoopTri *lt = &mdb->cagemesh_cache.looptri[hit.index]; - const MPoly *mp = &mdb->cagemesh_cache.mpoly[lt->poly]; + const MPoly *mp = &mdb->cagemesh_cache.polys[lt->poly]; const float(*cagecos)[3] = mdb->cagecos; const float len = isect_mdef.lambda; MDefBoundIsect *isect; @@ -1058,7 +1057,7 @@ static MDefBoundIsect *meshdeform_ray_tree_intersect(MeshDeformBind *mdb, /* compute mean value coordinates for interpolation */ for (int i = 0; i < mp->totloop; i++) { - copy_v3_v3(mp_cagecos[i], cagecos[mloop[mp->loopstart + i].v]); + copy_v3_v3(mp_cagecos[i], cagecos[loops[mp->loopstart + i].v]); } interp_weights_poly_v3(isect->poly_weights, @@ -1227,11 +1226,11 @@ static float meshdeform_boundary_phi(const MeshDeformBind *mdb, const MDefBoundIsect *isect, int cagevert) { - const MLoop *mloop = mdb->cagemesh_cache.mloop; - const MPoly *mp = &mdb->cagemesh_cache.mpoly[isect->poly_index]; + const blender::Span loops = mdb->cagemesh_cache.loops; + const MPoly *mp = &mdb->cagemesh_cache.polys[isect->poly_index]; for (int i = 0; i < mp->totloop; i++) { - if (mloop[mp->loopstart + i].v == cagevert) { + if (loops[mp->loopstart + i].v == cagevert) { return isect->poly_weights[i]; } } @@ -1631,8 +1630,8 @@ static void harmonic_coordinates_bind(MeshDeformModifierData *mmd, MeshDeformBin /* initialize data from 'cagedm' for reuse */ { Mesh *me = mdb->cagemesh; - mdb->cagemesh_cache.mpoly = BKE_mesh_polys(me); - mdb->cagemesh_cache.mloop = BKE_mesh_loops(me); + mdb->cagemesh_cache.polys = me->polys(); + mdb->cagemesh_cache.loops = me->loops(); mdb->cagemesh_cache.looptri = BKE_mesh_runtime_looptri_ensure(me); mdb->cagemesh_cache.poly_nors = BKE_mesh_poly_normals_ensure(me); } @@ -1769,14 +1768,12 @@ void ED_mesh_deform_bind_callback(Object *object, { MeshDeformModifierData *mmd_orig = (MeshDeformModifierData *)BKE_modifier_get_original( object, &mmd->modifier); - MeshDeformBind mdb; + MeshDeformBind mdb{}; int a; waitcursor(1); start_progress_bar(); - memset(&mdb, 0, sizeof(MeshDeformBind)); - /* No need to support other kinds of mesh data as binding is a one-off action. */ BKE_mesh_wrapper_ensure_mdata(cagemesh); diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc index 9aac90fc6e2..aab4bd12222 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -77,11 +77,7 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(const bool if (include_generated) { const EnumPropertyItem generated_items[] = { - {ASSET_LIBRARY_ALL, - "ALL", - ICON_BLANK1, - "All", - "Show assets from all of the listed asset libraries"}, + {ASSET_LIBRARY_ALL, "ALL", 0, "All", "Show assets from all of the listed asset libraries"}, RNA_ENUM_ITEM_SEPR, {ASSET_LIBRARY_LOCAL, "LOCAL", @@ -90,7 +86,7 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(const bool "Show the assets currently available in this Blender session"}, {ASSET_LIBRARY_ESSENTIALS, "ESSENTIALS", - ICON_BLANK1, + 0, "Essentials", "Show the basic building blocks and utilities coming with Blender"}, {0, nullptr, 0, nullptr, nullptr}, @@ -122,7 +118,7 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(const bool const int enum_value = ED_asset_library_reference_to_enum_value(&library_reference); /* Use library path as description, it's a nice hint for users. */ EnumPropertyItem tmp = { - enum_value, user_library->name, ICON_BLANK1, user_library->name, user_library->path}; + enum_value, user_library->name, ICON_NONE, user_library->name, user_library->path}; RNA_enum_item_add(&item, &totitem, &tmp); } diff --git a/source/blender/editors/curves/intern/curves_data.cc b/source/blender/editors/curves/intern/curves_data.cc index c11879a18f9..d178ee60890 100644 --- a/source/blender/editors/curves/intern/curves_data.cc +++ b/source/blender/editors/curves/intern/curves_data.cc @@ -3,7 +3,36 @@ #include "BKE_curves.hh" #include "BKE_geometry_fields.hh" +#include "BLI_task.hh" + +#include "DNA_object_types.h" + #include "ED_curves.h" +#include "ED_transverts.h" + +namespace blender::ed::curves { + +void transverts_from_curves_positions_create(bke::CurvesGeometry &curves, TransVertStore *tvs) +{ + Vector selected_indices; + IndexMask selection = retrieve_selected_points(curves, selected_indices); + MutableSpan positions = curves.positions_for_write(); + + tvs->transverts = static_cast( + MEM_calloc_arrayN(selection.size(), sizeof(TransVert), __func__)); + tvs->transverts_tot = selection.size(); + + threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange selection_range) { + for (const int point_i : selection_range) { + TransVert &tv = tvs->transverts[point_i]; + tv.loc = positions[selection[point_i]]; + tv.flag = SELECT; + copy_v3_v3(tv.oldloc, tv.loc); + } + }); +} + +} // namespace blender::ed::curves float (*ED_curves_point_normals_array_create(const Curves *curves_id))[3] { @@ -21,3 +50,10 @@ float (*ED_curves_point_normals_array_create(const Curves *curves_id))[3] return reinterpret_cast(data); } + +void ED_curves_transverts_create(Curves *curves_id, TransVertStore *tvs) +{ + using namespace blender; + bke::CurvesGeometry &curves = curves_id->geometry.wrap(); + ed::curves::transverts_from_curves_positions_create(curves, tvs); +} diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 080307ef2bd..a05ecfcaed3 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -1008,6 +1008,60 @@ static void CURVES_OT_select_linked(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static int select_more_exec(bContext *C, wmOperator * /*op*/) +{ + VectorSet unique_curves = get_unique_editable_curves(*C); + for (Curves *curves_id : unique_curves) { + CurvesGeometry &curves = curves_id->geometry.wrap(); + select_adjacent(curves, false); + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + + return OPERATOR_FINISHED; +} + +static void CURVES_OT_select_more(wmOperatorType *ot) +{ + ot->name = "Select More"; + ot->idname = __func__; + ot->description = "Grow the selection by one point"; + + ot->exec = select_more_exec; + ot->poll = editable_curves_point_domain_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int select_less_exec(bContext *C, wmOperator * /*op*/) +{ + VectorSet unique_curves = get_unique_editable_curves(*C); + for (Curves *curves_id : unique_curves) { + CurvesGeometry &curves = curves_id->geometry.wrap(); + select_adjacent(curves, true); + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + + return OPERATOR_FINISHED; +} + +static void CURVES_OT_select_less(wmOperatorType *ot) +{ + ot->name = "Select Less"; + ot->idname = __func__; + ot->description = "Shrink the selection by one point"; + + ot->exec = select_less_exec; + ot->poll = editable_curves_point_domain_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + namespace surface_set { static bool surface_set_poll(bContext *C) @@ -1133,6 +1187,8 @@ void ED_operatortypes_curves() WM_operatortype_append(CURVES_OT_select_random); WM_operatortype_append(CURVES_OT_select_end); WM_operatortype_append(CURVES_OT_select_linked); + WM_operatortype_append(CURVES_OT_select_more); + WM_operatortype_append(CURVES_OT_select_less); WM_operatortype_append(CURVES_OT_surface_set); WM_operatortype_append(CURVES_OT_delete); } diff --git a/source/blender/editors/curves/intern/curves_selection.cc b/source/blender/editors/curves/intern/curves_selection.cc index df990ed505b..559dd048957 100644 --- a/source/blender/editors/curves/intern/curves_selection.cc +++ b/source/blender/editors/curves/intern/curves_selection.cc @@ -272,6 +272,85 @@ void select_linked(bke::CurvesGeometry &curves) selection.finish(); } +void select_adjacent(bke::CurvesGeometry &curves, const bool deselect) +{ + const OffsetIndices points_by_curve = curves.points_by_curve(); + bke::GSpanAttributeWriter selection = ensure_selection_attribute( + curves, ATTR_DOMAIN_POINT, CD_PROP_BOOL); + const VArray cyclic = curves.cyclic(); + + if (deselect) { + invert_selection(selection.span); + } + + if (selection.span.type().is()) { + MutableSpan selection_typed = selection.span.typed(); + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = points_by_curve[curve_i]; + + /* Handle all cases in the forward direction. */ + for (int point_i = points.first(); point_i < points.last(); point_i++) { + if (!selection_typed[point_i] && selection_typed[point_i + 1]) { + selection_typed[point_i] = true; + } + } + + /* Handle all cases in the backwards direction. */ + for (int point_i = points.last(); point_i > points.first(); point_i--) { + if (!selection_typed[point_i] && selection_typed[point_i - 1]) { + selection_typed[point_i] = true; + } + } + + /* Handle cyclic curve case. */ + if (cyclic[curve_i]) { + if (selection_typed[points.first()] != selection_typed[points.last()]) { + selection_typed[points.first()] = true; + selection_typed[points.last()] = true; + } + } + } + }); + } + else if (selection.span.type().is()) { + MutableSpan selection_typed = selection.span.typed(); + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { + for (const int curve_i : range) { + const IndexRange points = points_by_curve[curve_i]; + + /* Handle all cases in the forward direction. */ + for (int point_i = points.first(); point_i < points.last(); point_i++) { + if ((selection_typed[point_i] == 0.0f) && (selection_typed[point_i + 1] > 0.0f)) { + selection_typed[point_i] = 1.0f; + } + } + + /* Handle all cases in the backwards direction. */ + for (int point_i = points.last(); point_i > points.first(); point_i--) { + if ((selection_typed[point_i] == 0.0f) && (selection_typed[point_i - 1] > 0.0f)) { + selection_typed[point_i] = 1.0f; + } + } + + /* Handle cyclic curve case. */ + if (cyclic[curve_i]) { + if (selection_typed[points.first()] != selection_typed[points.last()]) { + selection_typed[points.first()] = 1.0f; + selection_typed[points.last()] = 1.0f; + } + } + } + }); + } + + if (deselect) { + invert_selection(selection.span); + } + + selection.finish(); +} + void select_random(bke::CurvesGeometry &curves, const eAttrDomain selection_domain, uint32_t random_seed, diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 0a8130ffb56..d2b91d2bcb1 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -867,6 +867,7 @@ 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 diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h index a8019646b8b..dba87d7434a 100644 --- a/source/blender/editors/include/ED_curves.h +++ b/source/blender/editors/include/ED_curves.h @@ -12,6 +12,7 @@ struct UndoType; struct SelectPick_Params; struct ViewContext; struct rcti; +struct TransVertStore; #ifdef __cplusplus extern "C" { @@ -32,6 +33,11 @@ void ED_keymap_curves(struct wmKeyConfig *keyconf); */ float (*ED_curves_point_normals_array_create(const struct Curves *curves_id))[3]; +/** + * Wrapper for `transverts_from_curves_positions_create`. + */ +void ED_curves_transverts_create(struct Curves *curves_id, struct TransVertStore *tvs); + /** \} */ #ifdef __cplusplus @@ -56,6 +62,13 @@ bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curv VectorSet get_unique_editable_curves(const bContext &C); void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob); +/** + * Allocate an array of `TransVert` for cursor/selection snapping (See + * `ED_transverts_create_from_obedit` in `view3d_snap.c`). + * \note: the `TransVert` elements in \a tvs are expected to write to the positions of \a curves. + */ +void transverts_from_curves_positions_create(bke::CurvesGeometry &curves, TransVertStore *tvs); + /* -------------------------------------------------------------------- */ /** \name Poll Functions * \{ */ @@ -136,6 +149,11 @@ void select_ends(bke::CurvesGeometry &curves, int amount, bool end_points); */ void select_linked(bke::CurvesGeometry &curves); +/** + * (De)select all the adjacent points of the current selected points. + */ +void select_adjacent(bke::CurvesGeometry &curves, bool deselect); + /** * Select random points or curves. * diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index f027a46b470..5bd672072b1 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -218,6 +218,19 @@ typedef enum eKeyPasteOffset { KEYFRAME_PASTE_OFFSET_NONE, } eKeyPasteOffset; +typedef enum eKeyPasteValueOffset { + /* Paste keys with the first key matching the key left of the cursor. */ + KEYFRAME_PASTE_VALUE_OFFSET_LEFT_KEY, + /* Paste keys with the last key matching the key right of the cursor. */ + KEYFRAME_PASTE_VALUE_OFFSET_RIGHT_KEY, + /* Paste keys relative to the value of the curve under the cursor. */ + KEYFRAME_PASTE_VALUE_OFFSET_CFRA, + /* Paste values relative to the cursor position. */ + KEYFRAME_PASTE_VALUE_OFFSET_CURSOR, + /* Paste keys with the exact copied value. */ + KEYFRAME_PASTE_VALUE_OFFSET_NONE, +} eKeyPasteValueOffset; + typedef enum eKeyMergeMode { /* overlay existing with new keys */ KEYFRAME_PASTE_MERGE_MIX, @@ -427,6 +440,7 @@ short copy_animedit_keys(struct bAnimContext *ac, ListBase *anim_data); eKeyPasteError paste_animedit_keys(struct bAnimContext *ac, ListBase *anim_data, eKeyPasteOffset offset_mode, + eKeyPasteValueOffset value_offset_mode, eKeyMergeMode merge_mode, bool flip); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index cfb71564671..531fc03f285 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -570,9 +570,6 @@ bool ED_mesh_edge_is_loose(const struct Mesh *mesh, int index); void ED_mesh_uv_ensure(struct Mesh *me, const char *name); int ED_mesh_uv_add( struct Mesh *me, const char *name, bool active_set, bool do_init, struct ReportList *reports); -bool ED_mesh_uv_remove_index(struct Mesh *me, int n); -bool ED_mesh_uv_remove_active(struct Mesh *me); -bool ED_mesh_uv_remove_named(struct Mesh *me, const char *name); void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me); /** diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 066aef1886d..16117937000 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -239,6 +239,17 @@ enum { UI_BUT_OVERRIDDEN = 1u << 31u, }; +/** #uiBut.dragflag */ +enum { + /** By default only the left part of a button triggers dragging. A questionable design to make + * the icon but not other parts of the button draggable. Set this flag so the entire button can + * be dragged. */ + UI_BUT_DRAG_FULL_BUT = (1 << 0), + + /* --- Internal flags. --- */ + UI_BUT_DRAGPOIN_FREE = (1 << 1), +}; + /* Default font size for normal text. */ #define UI_DEFAULT_TEXT_POINTS 11.0f @@ -881,6 +892,9 @@ bool UI_but_flag_is_set(uiBut *but, int flag); void UI_but_drawflag_enable(uiBut *but, int flag); void UI_but_drawflag_disable(uiBut *but, int flag); +void UI_but_dragflag_enable(uiBut *but, int flag); +void UI_but_dragflag_disable(uiBut *but, int flag); + void UI_but_disable(uiBut *but, const char *disabled_hint); void UI_but_type_set_menu_from_pulldown(uiBut *but); @@ -1789,9 +1803,12 @@ void UI_but_drag_set_id(uiBut *but, struct ID *id); /** * Set an image to display while dragging. This works for any drag type (`WM_DRAG_XXX`). * Not to be confused with #UI_but_drag_set_image(), which sets up dragging of an image. + * + * Sets #UI_BUT_DRAG_FULL_BUT so the full button can be dragged. */ void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, float scale); /** + * Sets #UI_BUT_DRAG_FULL_BUT so the full button can be dragged. * \param asset: May be passed from a temporary variable, drag data only stores a copy of this. */ void UI_but_drag_set_asset(uiBut *but, @@ -1808,6 +1825,8 @@ void UI_but_drag_set_name(uiBut *but, const char *name); * Value from button itself. */ void UI_but_drag_set_value(uiBut *but); + +/** Sets #UI_BUT_DRAG_FULL_BUT so the full button can be dragged. */ void UI_but_drag_set_image( uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, bool use_free); diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 73da3b65ecd..f3142c6f001 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -996,9 +996,9 @@ static bool ui_but_update_from_old_block(const bContext *C, else { int flag_copy = UI_BUT_DRAG_MULTI; - /* Stupid special case: The active button may be inside (as in, overlapped on top) a view-item + /* Stupid special case: The active button may be inside (as in, overlapped on top) a row * button which we also want to keep highlighted then. */ - if (but->type == UI_BTYPE_VIEW_ITEM) { + if (ELEM(but->type, UI_BTYPE_VIEW_ITEM, UI_BTYPE_LISTROW)) { flag_copy |= UI_ACTIVE; } @@ -4224,6 +4224,10 @@ static uiBut *ui_def_but(uiBlock *block, but->flag |= UI_BUT_UNDO; } + if (ELEM(but->type, UI_BTYPE_COLOR)) { + but->dragflag |= UI_BUT_DRAG_FULL_BUT; + } + BLI_addtail(&block->buttons, but); if (block->curlayout) { @@ -4287,6 +4291,7 @@ static void ui_def_but_rna__menu(bContext * /*C*/, uiLayout *layout, void *but_p int totitems = 0; int categories = 0; int entries_nosepr_count = 0; + bool has_item_with_icon = false; for (const EnumPropertyItem *item = item_array; item->identifier; item++, totitems++) { if (!item->identifier[0]) { /* inconsistent, but menus with categories do not look good flipped */ @@ -4298,6 +4303,9 @@ static void ui_def_but_rna__menu(bContext * /*C*/, uiLayout *layout, void *but_p /* We do not want simple separators in `entries_nosepr_count`. */ continue; } + if (item->icon) { + has_item_with_icon = true; + } entries_nosepr_count++; } @@ -4402,11 +4410,18 @@ static void ui_def_but_rna__menu(bContext * /*C*/, uiLayout *layout, void *but_p uiItemS(column); } else { - if (item->icon) { + int icon = item->icon; + /* Use blank icon if there is none for this item (but for some other one) to make sure labels + * align. */ + if (icon == ICON_NONE && has_item_with_icon) { + icon = ICON_BLANK1; + } + + if (icon) { uiDefIconTextButI(block, UI_BTYPE_BUT_MENU, B_NOP, - item->icon, + icon, item->name, 0, 0, @@ -4585,15 +4600,7 @@ static uiBut *ui_def_but_rna(uiBlock *block, #endif } - /* #ICON_BLANK1 can be used to add padding of the size of an icon. This is fine to align - * multiple items within a menu, but not for the menu button that only shows the label then. - */ - if ((type == UI_BTYPE_MENU) && (item[i].icon == ICON_BLANK1)) { - icon = ICON_NONE; - } - else { - icon = item[i].icon; - } + icon = item[i].icon; } else { if (!str) { @@ -5887,6 +5894,16 @@ void UI_but_drawflag_disable(uiBut *but, int flag) but->drawflag &= ~flag; } +void UI_but_dragflag_enable(uiBut *but, int flag) +{ + but->dragflag |= flag; +} + +void UI_but_dragflag_disable(uiBut *but, int flag) +{ + but->dragflag &= ~flag; +} + void UI_but_disable(uiBut *but, const char *disabled_hint) { UI_but_flag_enable(but, UI_BUT_DISABLED); diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc index 462cef50317..4156ec0f4a2 100644 --- a/source/blender/editors/interface/interface_drag.cc +++ b/source/blender/editors/interface/interface_drag.cc @@ -24,6 +24,7 @@ void UI_but_drag_attach_image(uiBut *but, struct ImBuf *imb, const float scale) { but->imb = imb; but->imb_scale = scale; + UI_but_dragflag_enable(but, UI_BUT_DRAG_FULL_BUT); } void UI_but_drag_set_asset(uiBut *but, diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index 9a9c0d84160..4b6f0f0f2e0 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -9651,31 +9651,29 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi return retval; } -static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *region) +/* Handle mouse hover for Views and UiList rows. */ +static int ui_handle_viewlist_items_hover(const wmEvent *event, const ARegion *region) { - bool has_view_item = false; + bool has_item = false; LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - /* Avoid unnecessary work: view item buttons are assumed to be inside views. */ - if (BLI_listbase_is_empty(&block->views)) { - continue; - } - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (but->type == UI_BTYPE_VIEW_ITEM) { + if (ELEM(but->type, UI_BTYPE_VIEW_ITEM,UI_BTYPE_LISTROW)) { but->flag &= ~UI_ACTIVE; - has_view_item = true; + has_item = true; } } } - if (!has_view_item) { + if (!has_item) { /* Avoid unnecessary lookup. */ return WM_UI_HANDLER_CONTINUE; } - /* Always highlight the hovered view item, even if the mouse hovers another button inside of it. - */ + /* Always highlight the hovered view item, even if the mouse hovers another button inside. */ uiBut *hovered_row_but = ui_view_item_find_mouse_over(region, event->xy); + if (!hovered_row_but) { + hovered_row_but = ui_list_row_find_mouse_over(region, event->xy); + } if (hovered_row_but) { hovered_row_but->flag |= UI_ACTIVE; } @@ -11305,9 +11303,9 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void * /*userdat ui_blocks_set_tooltips(region, true); } - /* Always do this, to reliably update view item highlighting, even if the mouse hovers a button - * nested in the item (it's an overlapping layout). */ - ui_handle_view_items_hover(event, region); + /* Always do this, to reliably update view and uilist item highlighting, even if + * the mouse hovers a button nested in the item (it's an overlapping layout). */ + ui_handle_viewlist_items_hover(event, region); if (retval == WM_UI_HANDLER_CONTINUE) { retval = ui_handle_view_item_event(C, event, but, region); } diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index 68a81de4e4a..accb6ecab2e 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -86,11 +86,6 @@ enum { /* WARNING: rest of #uiBut.flag in UI_interface.h */ }; -/** #uiBut.dragflag */ -enum { - UI_BUT_DRAGPOIN_FREE = (1 << 0), -}; - /** #uiBut.pie_dir */ enum RadialDirection { UI_RADIAL_NONE = -1, diff --git a/source/blender/editors/interface/interface_panel.cc b/source/blender/editors/interface/interface_panel.cc index 48fa0b9f0a3..d1702f1a9d1 100644 --- a/source/blender/editors/interface/interface_panel.cc +++ b/source/blender/editors/interface/interface_panel.cc @@ -731,13 +731,7 @@ Panel *UI_panel_begin( UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); } - *r_open = false; - - if (UI_panel_is_closed(panel)) { - return panel; - } - - *r_open = true; + *r_open = !UI_panel_is_closed(panel); return panel; } diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index 84e2b488ccb..1d5eb9e7250 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -247,7 +247,7 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv BLI_rcti_rctf_copy(&rect, &but->rect); - if (but->imb || but->type == UI_BTYPE_COLOR) { + if (but->dragflag & UI_BUT_DRAG_FULL_BUT) { /* use button size itself */ } else if (but->drawflag & UI_BUT_ICON_LEFT) { diff --git a/source/blender/editors/interface/interface_region_search.cc b/source/blender/editors/interface/interface_region_search.cc index 55511b0cff7..46a5151a2a2 100644 --- a/source/blender/editors/interface/interface_region_search.cc +++ b/source/blender/editors/interface/interface_region_search.cc @@ -73,6 +73,9 @@ struct uiSearchboxData { rcti bbox; uiFontStyle fstyle; uiSearchItems items; + bool size_set; + ARegion *butregion; + uiButSearch *search_but; /** index in items array */ int active; /** when menu opened with enough space for this */ @@ -702,55 +705,35 @@ static void ui_searchbox_region_listen_fn(const wmRegionListenerParams *params) } } -static ARegion *ui_searchbox_create_generic_ex(bContext *C, - ARegion *butregion, - uiButSearch *but, - const bool use_shortcut_sep) +static uiMenuItemSeparatorType ui_searchbox_item_separator(uiSearchboxData *data) { - wmWindow *win = CTX_wm_window(C); - const uiStyle *style = UI_style_get(); - const float aspect = but->block->aspect; + uiMenuItemSeparatorType separator_type = data->use_shortcut_sep ? + UI_MENU_ITEM_SEPARATOR_SHORTCUT : + UI_MENU_ITEM_SEPARATOR_NONE; + if (separator_type == UI_MENU_ITEM_SEPARATOR_NONE && !data->preview) { + for (int a = 0; a < data->items.totitem; a++) { + if (data->items.but_flags[a] & UI_BUT_HAS_SEP_CHAR) { + separator_type = UI_MENU_ITEM_SEPARATOR_HINT; + break; + } + } + } + return separator_type; +} + +static void ui_searchbox_region_layout_fn(const struct bContext *C, struct ARegion *region) +{ + uiSearchboxData *data = (uiSearchboxData *)region->regiondata; + + if (data->size_set) { + /* Already set. */ + return; + } + + uiButSearch *but = data->search_but; + ARegion *butregion = data->butregion; const int margin = UI_POPUP_MARGIN; - - /* create area region */ - ARegion *region = ui_region_temp_add(CTX_wm_screen(C)); - - static ARegionType type; - memset(&type, 0, sizeof(ARegionType)); - type.draw = ui_searchbox_region_draw_fn; - type.free = ui_searchbox_region_free_fn; - type.listener = ui_searchbox_region_listen_fn; - type.regionid = RGN_TYPE_TEMPORARY; - region->type = &type; - - /* Create search-box data. */ - uiSearchboxData *data = MEM_cnew(__func__); - data->search_arg = but->arg; - data->search_listener = but->listen_fn; - - /* Set font, get the bounding-box. */ - data->fstyle = style->widget; /* copy struct */ - ui_fontscale(&data->fstyle.points, aspect); - UI_fontstyle_set(&data->fstyle); - - region->regiondata = data; - - /* Special case, hard-coded feature, not draw backdrop when called from menus, - * assume for design that popup already added it. */ - if (but->block->flag & UI_BLOCK_SEARCH_MENU) { - data->noback = true; - } - - if (but->a1 > 0 && but->a2 > 0) { - data->preview = true; - data->prv_rows = but->a1; - data->prv_cols = but->a2; - } - - if (but->optype != nullptr || use_shortcut_sep) { - data->use_shortcut_sep = true; - } - data->sep_string = but->item_sep_string; + wmWindow *win = CTX_wm_window(C); /* compute position */ if (but->block->flag & UI_BLOCK_SEARCH_MENU) { @@ -775,7 +758,12 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, } } else { - const int searchbox_width = UI_searchbox_size_x(); + int searchbox_width = UI_searchbox_size_x(); + + /* We should make this wider if there is a path or hint on the right. */ + if (ui_searchbox_item_separator(data) != UI_MENU_ITEM_SEPARATOR_NONE) { + searchbox_width += 12 * data->fstyle.points * U.dpi_fac; + } rctf rect_fl; rect_fl.xmin = but->rect.xmin - 5; /* align text with button */ @@ -845,6 +833,64 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, region->winrct.ymax = rect_i.ymax; } + region->winx = region->winrct.xmax - region->winrct.xmin; + region->winy = region->winrct.ymax - region->winrct.ymin; + + data->size_set = true; +} + +static ARegion *ui_searchbox_create_generic_ex(bContext *C, + ARegion *butregion, + uiButSearch *but, + const bool use_shortcut_sep) +{ + const uiStyle *style = UI_style_get(); + const float aspect = but->block->aspect; + + /* create area region */ + ARegion *region = ui_region_temp_add(CTX_wm_screen(C)); + + static ARegionType type; + memset(&type, 0, sizeof(ARegionType)); + type.layout = ui_searchbox_region_layout_fn; + type.draw = ui_searchbox_region_draw_fn; + type.free = ui_searchbox_region_free_fn; + type.listener = ui_searchbox_region_listen_fn; + type.regionid = RGN_TYPE_TEMPORARY; + region->type = &type; + + /* Create search-box data. */ + uiSearchboxData *data = MEM_cnew(__func__); + data->search_arg = but->arg; + data->search_but = but; + data->butregion = butregion; + data->size_set = false; + data->search_listener = but->listen_fn; + + /* Set font, get the bounding-box. */ + data->fstyle = style->widget; /* copy struct */ + ui_fontscale(&data->fstyle.points, aspect); + UI_fontstyle_set(&data->fstyle); + + region->regiondata = data; + + /* Special case, hard-coded feature, not draw backdrop when called from menus, + * assume for design that popup already added it. */ + if (but->block->flag & UI_BLOCK_SEARCH_MENU) { + data->noback = true; + } + + if (but->a1 > 0 && but->a2 > 0) { + data->preview = true; + data->prv_rows = but->a1; + data->prv_cols = but->a2; + } + + if (but->optype != nullptr || use_shortcut_sep) { + data->use_shortcut_sep = true; + } + data->sep_string = but->item_sep_string; + /* adds subwindow */ ED_region_floating_init(region); diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc index 55ca945671f..2ac8e96b996 100644 --- a/source/blender/editors/interface/interface_template_attribute_search.cc +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -92,15 +92,9 @@ void attribute_search_add_items(StringRefNull str, StringSearch *search = BLI_string_search_new(); for (const GeometryAttributeInfo *item : infos) { - - /* Don't show the legacy "normal" attribute. */ - if (item->name == "normal" && item->domain == ATTR_DOMAIN_FACE) { - continue; - } if (!bke::allow_procedural_attribute_access(item->name)) { continue; } - BLI_string_search_add(search, item->name.c_str(), (void *)item, 0); } diff --git a/source/blender/editors/interface/interface_widgets.cc b/source/blender/editors/interface/interface_widgets.cc index 086b583d936..b6b512be703 100644 --- a/source/blender/editors/interface/interface_widgets.cc +++ b/source/blender/editors/interface/interface_widgets.cc @@ -3958,18 +3958,6 @@ static void widget_textbut(uiWidgetColors *wcol, widgetbase_draw(&wtb, wcol); } -static void widget_preview_tile(uiBut *but, - uiWidgetColors *wcol, - rcti *rect, - const uiWidgetStateInfo * /*state*/, - int /*roundboxalign*/, - const float /*zoom*/) -{ - const uiStyle *style = UI_style_get(); - ui_draw_preview_item_stateless( - &style->widget, rect, but->drawstr, but->icon, wcol->text, UI_STYLE_TEXT_CENTER); -} - static void widget_menuiconbut(uiWidgetColors *wcol, rcti *rect, const uiWidgetStateInfo * /*state*/, @@ -4092,7 +4080,7 @@ static void widget_menu_radial_itembut(uiBut *but, static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, - const uiWidgetStateInfo * /*state*/, + const uiWidgetStateInfo *state, int /*roundboxalign*/, const float zoom) { @@ -4104,9 +4092,27 @@ static void widget_list_itembut(uiWidgetColors *wcol, const float rad = widget_radius_from_zoom(zoom, wcol); round_box_edges(&wtb, UI_CNR_ALL, rect, rad); + if (state->but_flag & UI_ACTIVE && !(state->but_flag & UI_SELECT)) { + copy_v3_v3_uchar(wcol->inner, wcol->text); + wcol->inner[3] = 20; + } + widgetbase_draw(&wtb, wcol); } +static void widget_preview_tile(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) +{ + widget_list_itembut(wcol, rect, state, roundboxalign, zoom); + + ui_draw_preview_item_stateless( + &UI_style_get()->widget, rect, but->drawstr, but->icon, wcol->text, UI_STYLE_TEXT_CENTER); +} + static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, const uiWidgetStateInfo *state, @@ -4511,6 +4517,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) /* Drawn via the `custom` callback. */ wt.text = nullptr; wt.custom = widget_preview_tile; + wt.wcol_theme = &btheme->tui.wcol_list_item; break; case UI_WTYPE_SWATCH: diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc index c479145e8df..6729a1ad035 100644 --- a/source/blender/editors/mesh/editface.cc +++ b/source/blender/editors/mesh/editface.cc @@ -212,106 +212,142 @@ void paintface_reveal(bContext *C, Object *ob, const bool select) paintface_flush_flags(C, ob, true, true); } -/* Set object-mode face selection seams based on edge data, uses hash table to find seam edges. */ - -static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bool select) +/** + * Join all edges of each poly in the AtomicDisjointSet. This can be used to find out which polys + * are connected to each other. + * \param islands Is expected to be of length mesh->totedge. + * \param skip_seams Polys separated by a seam will be treated as not connected. + */ +static void build_poly_connections(blender::AtomicDisjointSet &islands, + Mesh &mesh, + const bool skip_seams = true) { using namespace blender; - bool do_it = true; - bool mark = false; + const Span polys = mesh.polys(); + const Span edges = mesh.edges(); + const Span loops = mesh.loops(); - BLI_bitmap *edge_tag = BLI_BITMAP_NEW(me->totedge, __func__); - BLI_bitmap *poly_tag = BLI_BITMAP_NEW(me->totpoly, __func__); - - const Span edges = me->edges(); - const Span polys = me->polys(); - const Span loops = me->loops(); - bke::MutableAttributeAccessor attributes = me->attributes_for_write(); + bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); const VArray hide_poly = attributes.lookup_or_default( ".hide_poly", ATTR_DOMAIN_FACE, false); + + /* Polys are connected if they share edges. By connecting all edges of a loop (as long as they + * are not a seam) we can find connected faces. */ + threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) { + + for (const int poly_index : range) { + if (hide_poly[poly_index]) { + continue; + } + const MPoly &poly = polys[poly_index]; + const Span poly_loops = loops.slice(poly.loopstart, poly.totloop); + + for (const int poly_loop_index : poly_loops.index_range()) { + const MLoop &outer_mloop = poly_loops[poly_loop_index]; + if (skip_seams && (edges[outer_mloop.e].flag & ME_SEAM) != 0) { + continue; + } + + for (const MLoop &inner_mloop : + poly_loops.slice(poly_loop_index, poly_loops.size() - poly_loop_index)) { + if (&outer_mloop == &inner_mloop) { + continue; + } + if (skip_seams && (edges[inner_mloop.e].flag & ME_SEAM) != 0) { + continue; + } + islands.join(inner_mloop.e, outer_mloop.e); + } + } + + } + + }); +} + +/* Select faces connected to the given face_indices. Seams are treated as separation. */ +static void paintface_select_linked_faces(Mesh &mesh, + const blender::Span face_indices, + const bool select) +{ + using namespace blender; + + AtomicDisjointSet islands(mesh.totedge); + build_poly_connections(islands, mesh); + + const Span polys = mesh.polys(); + const Span edges = mesh.edges(); + const Span loops = mesh.loops(); + + bke::MutableAttributeAccessor attributes = mesh.attributes_for_write(); bke::SpanAttributeWriter select_poly = attributes.lookup_or_add_for_write_span( ".select_poly", ATTR_DOMAIN_FACE); - if (index != uint(-1)) { - /* only put face under cursor in array */ - const MPoly &poly = polys[index]; - BKE_mesh_poly_edgebitmap_insert(edge_tag, &poly, &loops[poly.loopstart]); - BLI_BITMAP_ENABLE(poly_tag, index); - } - else { - /* fill array by selection */ - for (int i = 0; i < me->totpoly; i++) { - if (hide_poly[i]) { - /* pass */ - } - else if (select_poly.span[i]) { - const MPoly &poly = polys[i]; - BKE_mesh_poly_edgebitmap_insert(edge_tag, &poly, &loops[poly.loopstart]); - BLI_BITMAP_ENABLE(poly_tag, i); - } - } - } - - while (do_it) { - do_it = false; - - /* expand selection */ - for (int i = 0; i < me->totpoly; i++) { - if (hide_poly[i]) { + Set selected_roots; + for (const int i : face_indices) { + const MPoly &poly = polys[i]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + if ((edges[loop.e].flag & ME_SEAM) != 0) { continue; } + const int root = islands.find_root(loop.e); + selected_roots.add(root); + } + } - if (!BLI_BITMAP_TEST(poly_tag, i)) { - mark = false; - - const MPoly &poly = polys[i]; - const MLoop *ml = &loops[poly.loopstart]; - for (int b = 0; b < poly.totloop; b++, ml++) { - if ((edges[ml->e].flag & ME_SEAM) == 0) { - if (BLI_BITMAP_TEST(edge_tag, ml->e)) { - mark = true; - break; - } - } - } - - if (mark) { - BLI_BITMAP_ENABLE(poly_tag, i); - BKE_mesh_poly_edgebitmap_insert(edge_tag, &poly, &loops[poly.loopstart]); - do_it = true; + threading::parallel_for(select_poly.span.index_range(), 1024, [&](const IndexRange range) { + for (const int poly_index : range) { + const MPoly &poly = polys[poly_index]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + const int root = islands.find_root(loop.e); + if (selected_roots.contains(root)) { + select_poly.span[poly_index] = select; + break; } } } - } + }); - MEM_freeN(edge_tag); - - for (int i = 0; i < me->totpoly; i++) { - if (BLI_BITMAP_TEST(poly_tag, i)) { - select_poly.span[i] = select; - } - } - - MEM_freeN(poly_tag); + select_poly.finish(); } void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const bool select) { - uint index = uint(-1); - + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr || me->totpoly == 0) { return; } + bke::MutableAttributeAccessor attributes = me->attributes_for_write(); + bke::SpanAttributeWriter select_poly = attributes.lookup_or_add_for_write_span( + ".select_poly", ATTR_DOMAIN_FACE); + + Vector indices; if (mval) { + uint index = uint(-1); if (!ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { + select_poly.finish(); return; } + /* Since paintface_select_linked_faces might not select the face under the cursor, select it + * here. */ + select_poly.span[index] = true; + indices.append(index); + } + + else { + for (const int i : select_poly.span.index_range()) { + if (!select_poly.span[i]) { + continue; + } + indices.append(i); + } } - select_linked_tfaces_with_seams(me, index, select); + select_poly.finish(); + paintface_select_linked_faces(*me, indices, select); paintface_flush_flags(C, ob, true, false); } diff --git a/source/blender/editors/mesh/editmesh_mask_extract.cc b/source/blender/editors/mesh/editmesh_mask_extract.cc index 83580b32884..8dc08f233e9 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.cc +++ b/source/blender/editors/mesh/editmesh_mask_extract.cc @@ -285,7 +285,7 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) params.add_solidify = RNA_boolean_get(op->ptr, "add_solidify"); /* Push an undo step prior to extraction. - * Note: A second push happens after the operator due to + * NOTE: A second push happens after the operator due to * the OPTYPE_UNDO flag; having an initial undo step here * is just needed to preserve the active object pointer. * diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index bedc3802f50..320906ff4d6 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -114,32 +114,6 @@ static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_t } #define GET_CD_DATA(me, data) ((me)->edit_mesh ? &(me)->edit_mesh->bm->data : &(me)->data) -static void delete_customdata_layer(Mesh *me, CustomDataLayer *layer) -{ - const int type = layer->type; - CustomData *data; - int layer_index, tot, n; - - char htype = BM_FACE; - if (ELEM(type, CD_PROP_BYTE_COLOR, CD_PROP_FLOAT2)) { - htype = BM_LOOP; - } - else if (ELEM(type, CD_PROP_COLOR)) { - htype = BM_VERT; - } - - data = mesh_customdata_get_type(me, htype, &tot); - layer_index = CustomData_get_layer_index(data, type); - n = (layer - &data->layers[layer_index]); - BLI_assert(n >= 0 && (n + layer_index) < data->totlayer); - - if (me->edit_mesh) { - BM_data_layer_free_n(me->edit_mesh->bm, data, type, n); - } - else { - CustomData_free_layer(data, type, tot, layer_index + n); - } -} static void mesh_uv_reset_array(float **fuv, const int len) { @@ -234,8 +208,8 @@ void ED_mesh_uv_loop_reset_ex(Mesh *me, const int layernum) float2 *mloopuv = static_cast( CustomData_get_layer_n_for_write(&me->ldata, CD_PROP_FLOAT2, layernum, me->totloop)); - const MPoly *polys = BKE_mesh_polys(me); - for (int i = 0; i < me->totpoly; i++) { + const blender::Span polys = me->polys(); + for (const int i : polys.index_range()) { mesh_uv_reset_mface(&polys[i], mloopuv); } } @@ -417,46 +391,6 @@ void ED_mesh_uv_ensure(Mesh *me, const char *name) } } -bool ED_mesh_uv_remove_index(Mesh *me, const int n) -{ - CustomData *ldata = GET_CD_DATA(me, ldata); - CustomDataLayer *cdlu; - int index; - - index = CustomData_get_layer_index_n(ldata, CD_PROP_FLOAT2, n); - cdlu = (index == -1) ? nullptr : &ldata->layers[index]; - - if (!cdlu) { - return false; - } - - delete_customdata_layer(me, cdlu); - - DEG_id_tag_update(&me->id, 0); - WM_main_add_notifier(NC_GEOM | ND_DATA, me); - - return true; -} -bool ED_mesh_uv_remove_active(Mesh *me) -{ - CustomData *ldata = GET_CD_DATA(me, ldata); - const int n = CustomData_get_active_layer(ldata, CD_PROP_FLOAT2); - - if (n != -1) { - return ED_mesh_uv_remove_index(me, n); - } - return false; -} -bool ED_mesh_uv_remove_named(Mesh *me, const char *name) -{ - CustomData *ldata = GET_CD_DATA(me, ldata); - const int n = CustomData_get_named_layer(ldata, CD_PROP_FLOAT2, name); - if (n != -1) { - return ED_mesh_uv_remove_index(me, n); - } - return false; -} - int ED_mesh_color_add( Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) { diff --git a/source/blender/editors/object/object_bake_api.cc b/source/blender/editors/object/object_bake_api.cc index d3b8e0e0eed..2a3b64ccd0a 100644 --- a/source/blender/editors/object/object_bake_api.cc +++ b/source/blender/editors/object/object_bake_api.cc @@ -978,8 +978,8 @@ static bool bake_targets_init_vertex_colors(Main *bmain, return true; } -static int find_original_loop(const MPoly *orig_polys, - const MLoop *orig_loops, +static int find_original_loop(const blender::Span orig_polys, + const blender::Span orig_loops, const int *vert_origindex, const int *poly_origindex, const int poly_eval, @@ -995,8 +995,8 @@ static int find_original_loop(const MPoly *orig_polys, } /* Find matching loop with original vertex in original polygon. */ - const MPoly *mpoly_orig = orig_polys + poly_orig; - const MLoop *mloop_orig = orig_loops + mpoly_orig->loopstart; + const MPoly *mpoly_orig = &orig_polys[poly_orig]; + const MLoop *mloop_orig = &orig_loops[mpoly_orig->loopstart]; for (int j = 0; j < mpoly_orig->totloop; ++j, ++mloop_orig) { if (mloop_orig->v == vert_orig) { return mpoly_orig->loopstart + j; @@ -1033,9 +1033,9 @@ static void bake_targets_populate_pixels_color_attributes(BakeTargets *targets, const int tottri = poly_to_tri_count(me_eval->totpoly, me_eval->totloop); MLoopTri *looptri = static_cast(MEM_mallocN(sizeof(*looptri) * tottri, __func__)); - const MLoop *loops = BKE_mesh_loops(me_eval); - BKE_mesh_recalc_looptri(loops, - BKE_mesh_polys(me_eval), + const blender::Span loops = me_eval->loops(); + BKE_mesh_recalc_looptri(loops.data(), + me_eval->polys().data(), BKE_mesh_vert_positions(me_eval), me_eval->totloop, me_eval->totpoly, @@ -1046,8 +1046,8 @@ static void bake_targets_populate_pixels_color_attributes(BakeTargets *targets, CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX)); const int *poly_origindex = static_cast( CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX)); - const MPoly *orig_polys = BKE_mesh_polys(me); - const MLoop *orig_loops = BKE_mesh_loops(me); + const blender::Span orig_polys = me->polys(); + const blender::Span orig_loops = me->loops(); for (int i = 0; i < tottri; i++) { const MLoopTri *lt = &looptri[i]; @@ -1156,9 +1156,9 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob) MEM_callocN(sizeof(int) * me->totvert, "num_loops_for_vertex")); memset(mcol, 0, sizeof(MPropCol) * me->totvert); - const MLoop *mloop = BKE_mesh_loops(me); - for (int i = 0; i < totloop; i++, mloop++) { - const int v = mloop->v; + const blender::Span loops = me->loops(); + for (int i = 0; i < totloop; i++) { + const int v = loops[i].v; bake_result_add_to_rgba(mcol[v].color, &result[i * channels_num], channels_num); num_loops_for_vertex[v]++; } diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index a808ca1337b..dd1ea454a66 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -819,7 +819,7 @@ static Mesh *remesh_symmetry_mirror(Object *ob, Mesh *mesh, eSymmetryAxes symmet mmd.flag &= MOD_MIR_AXIS_X << i; mesh_mirror_temp = mesh_mirror; mesh_mirror = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier( - &mmd, ob, mesh_mirror, axis, true); + &mmd, ob, mesh_mirror, axis, true, nullptr, nullptr); if (mesh_mirror_temp != mesh_mirror) { BKE_id_free(nullptr, mesh_mirror_temp); } diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt index bf236192fd8..77ea72357a8 100644 --- a/source/blender/editors/screen/CMakeLists.txt +++ b/source/blender/editors/screen/CMakeLists.txt @@ -24,7 +24,7 @@ set(INC_SYS ) set(SRC - area.c + area.cc area_query.c area_utils.c glutil.c diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.cc similarity index 92% rename from source/blender/editors/screen/area.c rename to source/blender/editors/screen/area.cc index 574cefceb36..e899a25fb13 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.cc @@ -71,10 +71,11 @@ enum RegionEmbossSide { static void region_draw_emboss(const ARegion *region, const rcti *scirct, int sides) { /* translate scissor rect to region space */ - const rcti rect = {.xmin = scirct->xmin - region->winrct.xmin, - .xmax = scirct->xmax - region->winrct.xmin, - .ymin = scirct->ymin - region->winrct.ymin, - .ymax = scirct->ymax - region->winrct.ymin}; + rcti rect{}; + rect.xmin = scirct->xmin - region->winrct.xmin; + rect.xmax = scirct->xmax - region->winrct.xmin; + rect.ymin = scirct->ymin - region->winrct.ymin; + rect.ymax = scirct->ymax - region->winrct.ymin; /* set transp line */ GPU_blend(GPU_BLEND_ALPHA); @@ -177,8 +178,7 @@ void ED_area_do_refresh(bContext *C, ScrArea *area) /** * \brief Corner widget use for quitting full-screen. */ -static void area_draw_azone_fullscreen( - short UNUSED(x1), short UNUSED(y1), short x2, short y2, float alpha) +static void area_draw_azone_fullscreen(short /*x1*/, short /*y1*/, short x2, short y2, float alpha) { UI_icon_draw_ex(x2 - U.widget_unit, y2 - U.widget_unit, @@ -186,7 +186,7 @@ static void area_draw_azone_fullscreen( U.inv_dpi_fac, min_ff(alpha, 0.75f), 0.0f, - NULL, + nullptr, false, UI_NO_ICON_OVERLAY_TEXT); } @@ -194,7 +194,7 @@ static void area_draw_azone_fullscreen( /** * \brief Corner widgets use for dragging and splitting the view. */ -static void area_draw_azone(short UNUSED(x1), short UNUSED(y1), short UNUSED(x2), short UNUSED(y2)) +static void area_draw_azone(short /*x1*/, short /*y1*/, short /*x2*/, short /*y2*/) { /* No drawing needed since all corners are action zone, and visually distinguishable. */ } @@ -281,18 +281,14 @@ static void region_draw_azone_tab_arrow(ScrArea *area, ARegion *region, AZone *a /* Workaround for different color spaces between normal areas and the ones using GPUViewports. */ float alpha = WM_region_use_viewport(area, region) ? 0.6f : 0.4f; const float color[4] = {0.05f, 0.05f, 0.05f, alpha}; - UI_draw_roundbox_aa( - &(const rctf){ - .xmin = (float)az->x1, - .xmax = (float)az->x2, - .ymin = (float)az->y1, - .ymax = (float)az->y2, - }, - true, - 4.0f, - color); + rctf rect{}; + rect.xmin = float(az->x1); + rect.xmax = float(az->x2); + rect.ymin = float(az->y1); + rect.ymax = float(az->y2); + UI_draw_roundbox_aa(&rect, true, 4.0f, color); - draw_azone_arrow((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, az->edge); + draw_azone_arrow(float(az->x1), float(az->y1), float(az->x2), float(az->y2), az->edge); } static void area_azone_tag_update(ScrArea *area) @@ -317,7 +313,7 @@ static void region_draw_azones(ScrArea *area, ARegion *region) rcti azrct; BLI_rcti_init(&azrct, az->x1, az->x2, az->y1, az->y2); - if (BLI_rcti_isect(®ion->drawrct, &azrct, NULL)) { + if (BLI_rcti_isect(®ion->drawrct, &azrct, nullptr)) { if (az->type == AZONE_AREA) { area_draw_azone(az->x1, az->y1, az->x2, az->y2); } @@ -374,16 +370,12 @@ static void region_draw_status_text(ScrArea *area, ARegion *region) float color[4] = {0.0f, 0.0f, 0.0f, 0.5f}; UI_GetThemeColor3fv(TH_BACK, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa( - &(const rctf){ - .xmin = x1, - .xmax = x2, - .ymin = y1, - .ymax = y2, - }, - true, - 4.0f, - color); + rctf rect{}; + rect.xmin = x1; + rect.xmax = x2; + rect.ymin = y1; + rect.ymax = y2; + UI_draw_roundbox_aa(&rect, true, 4.0f, color); UI_FontThemeColor(fontid, TH_TEXT); } @@ -397,11 +389,11 @@ static void region_draw_status_text(ScrArea *area, ARegion *region) void ED_region_do_msg_notify_tag_redraw( /* Follow wmMsgNotifyFn spec */ - bContext *UNUSED(C), - wmMsgSubscribeKey *UNUSED(msg_key), + bContext * /*C*/, + wmMsgSubscribeKey * /*msg_key*/, wmMsgSubscribeValue *msg_val) { - ARegion *region = msg_val->owner; + ARegion *region = static_cast(msg_val->owner); ED_region_tag_redraw(region); /* This avoids _many_ situations where header/properties control display settings. @@ -420,33 +412,32 @@ void ED_region_do_msg_notify_tag_redraw( void ED_area_do_msg_notify_tag_refresh( /* Follow wmMsgNotifyFn spec */ - bContext *UNUSED(C), - wmMsgSubscribeKey *UNUSED(msg_key), + bContext * /*C*/, + wmMsgSubscribeKey * /*msg_key*/, wmMsgSubscribeValue *msg_val) { - ScrArea *area = msg_val->user_data; + ScrArea *area = static_cast(msg_val->user_data); ED_area_tag_refresh(area); } void ED_area_do_mgs_subscribe_for_tool_header(const wmRegionMessageSubscribeParams *params) { - struct wmMsgBus *mbus = params->message_bus; + wmMsgBus *mbus = params->message_bus; WorkSpace *workspace = params->workspace; ARegion *region = params->region; BLI_assert(region->regiontype == RGN_TYPE_TOOL_HEADER); - wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { - .owner = region, - .user_data = region, - .notify = ED_region_do_msg_notify_tag_redraw, - }; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw{}; + msg_sub_value_region_tag_redraw.owner = region; + msg_sub_value_region_tag_redraw.user_data = region; + msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw; WM_msg_subscribe_rna_prop( mbus, &workspace->id, workspace, WorkSpace, tools, &msg_sub_value_region_tag_redraw); } void ED_area_do_mgs_subscribe_for_tool_ui(const wmRegionMessageSubscribeParams *params) { - struct wmMsgBus *mbus = params->message_bus; + wmMsgBus *mbus = params->message_bus; WorkSpace *workspace = params->workspace; ARegion *region = params->region; @@ -470,11 +461,10 @@ void ED_area_do_mgs_subscribe_for_tool_ui(const wmRegionMessageSubscribeParams * } if (update_region) { - wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { - .owner = region, - .user_data = region, - .notify = ED_region_do_msg_notify_tag_redraw, - }; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw{}; + msg_sub_value_region_tag_redraw.owner = region; + msg_sub_value_region_tag_redraw.user_data = region; + msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw; WM_msg_subscribe_rna_prop( mbus, &workspace->id, workspace, WorkSpace, tools, &msg_sub_value_region_tag_redraw); } @@ -611,7 +601,7 @@ void ED_region_do_draw(bContext *C, ARegion *region) wmWindowManager *wm = CTX_wm_manager(C); bScreen *screen = WM_window_get_active_screen(win); Scene *scene = CTX_data_scene(C); - struct wmMsgBus *mbus = wm->message_bus; + wmMsgBus *mbus = wm->message_bus; WM_msgbus_clear_by_owner(mbus, region); /* Cheat, always subscribe to this space type properties. @@ -620,29 +610,27 @@ void ED_region_do_draw(bContext *C, ARegion *region) */ if (ELEM( region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS, RGN_TYPE_UI, RGN_TYPE_TOOLS)) { - SpaceLink *sl = area->spacedata.first; + SpaceLink *sl = static_cast(area->spacedata.first); PointerRNA ptr; RNA_pointer_create(&screen->id, &RNA_Space, sl, &ptr); - wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { - .owner = region, - .user_data = region, - .notify = ED_region_do_msg_notify_tag_redraw, - }; /* All properties for this space type. */ - WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_region_tag_redraw, __func__); + wmMsgSubscribeValue msg_sub_value_region_tag_redraw{}; + msg_sub_value_region_tag_redraw.owner = region; + msg_sub_value_region_tag_redraw.user_data = region; + msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw; + WM_msg_subscribe_rna(mbus, &ptr, nullptr, &msg_sub_value_region_tag_redraw, __func__); } - wmRegionMessageSubscribeParams message_subscribe_params = { - .context = C, - .message_bus = mbus, - .workspace = workspace, - .scene = scene, - .screen = screen, - .area = area, - .region = region, - }; + wmRegionMessageSubscribeParams message_subscribe_params{}; + message_subscribe_params.context = C; + message_subscribe_params.message_bus = mbus; + message_subscribe_params.workspace = workspace; + message_subscribe_params.scene = scene; + message_subscribe_params.screen = screen; + message_subscribe_params.area = area; + message_subscribe_params.region = region; ED_region_message_subscribe(&message_subscribe_params); } } @@ -687,7 +675,7 @@ void ED_region_tag_refresh_ui(ARegion *region) } } -void ED_region_tag_redraw_editor_overlays(struct ARegion *region) +void ED_region_tag_redraw_editor_overlays(ARegion *region) { if (region && !(region->do_draw & (RGN_DRAWING | RGN_DRAW))) { if (region->do_draw & RGN_DRAW_PARTIAL) { @@ -768,13 +756,13 @@ const char *ED_area_region_search_filter_get(const ScrArea *area, const ARegion { /* Only the properties editor has a search string for now. */ if (area->spacetype == SPACE_PROPERTIES) { - SpaceProperties *sbuts = area->spacedata.first; + SpaceProperties *sbuts = static_cast(area->spacedata.first); if (region->regiontype == RGN_TYPE_WINDOW) { return ED_buttons_search_string_get(sbuts); } } - return NULL; + return nullptr; } void ED_region_search_filter_update(const ScrArea *area, ARegion *region) @@ -792,15 +780,15 @@ void ED_region_search_filter_update(const ScrArea *area, ARegion *region) void ED_area_status_text(ScrArea *area, const char *str) { /* happens when running transform operators in background mode */ - if (area == NULL) { + if (area == nullptr) { return; } LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == RGN_TYPE_HEADER) { if (str) { - if (region->headerstr == NULL) { - region->headerstr = MEM_mallocN(UI_MAX_DRAW_STR, "headerprint"); + if (region->headerstr == nullptr) { + region->headerstr = static_cast(MEM_mallocN(UI_MAX_DRAW_STR, "headerprint")); } BLI_strncpy(region->headerstr, str, UI_MAX_DRAW_STR); BLI_str_rstrip(region->headerstr); @@ -818,14 +806,14 @@ void ED_workspace_status_text(bContext *C, const char *str) wmWindow *win = CTX_wm_window(C); WorkSpace *workspace = CTX_wm_workspace(C); - /* Can be NULL when running operators in background mode. */ - if (workspace == NULL) { + /* Can be nullptr when running operators in background mode. */ + if (workspace == nullptr) { return; } if (str) { - if (workspace->status_text == NULL) { - workspace->status_text = MEM_mallocN(UI_MAX_DRAW_STR, "headerprint"); + if (workspace->status_text == nullptr) { + workspace->status_text = static_cast(MEM_mallocN(UI_MAX_DRAW_STR, "headerprint")); } BLI_strncpy(workspace->status_text, str, UI_MAX_DRAW_STR); } @@ -945,7 +933,7 @@ static void region_azone_edge(AZone *az, ARegion *region) { /* If region is overlapped (transparent background), move #AZone to content. * Note this is an arbitrary amount that matches nicely with numbers elsewhere. */ - int overlap_padding = (region->overlap) ? (int)(0.4f * U.widget_unit) : 0; + int overlap_padding = (region->overlap) ? int(0.4f * U.widget_unit) : 0; switch (az->edge) { case AE_TOP_TO_BOTTOMRIGHT: @@ -1067,7 +1055,7 @@ static void region_azone_scrollbar_init(ScrArea *area, AZScrollDirection direction) { rcti scroller_vert = (direction == AZ_SCROLL_VERT) ? region->v2d.vert : region->v2d.hor; - AZone *az = MEM_callocN(sizeof(*az), __func__); + AZone *az = static_cast(MEM_callocN(sizeof(*az), __func__)); float hide_width; BLI_addtail(&area->actionzones, az); @@ -1184,7 +1172,7 @@ static void region_overlap_fix(ScrArea *area, ARegion *region) continue; } align1 = region_iter->alignment; - if (BLI_rcti_isect(®ion_iter->winrct, ®ion->winrct, NULL)) { + if (BLI_rcti_isect(®ion_iter->winrct, ®ion->winrct, nullptr)) { if (align1 != align) { /* Left overlapping right or vice-versa, forbid this! */ region->flag |= RGN_FLAG_TOO_SMALL; @@ -1229,7 +1217,7 @@ static void region_overlap_fix(ScrArea *area, ARegion *region) if (region_iter->overlap && (region_iter->alignment & RGN_SPLIT_PREV) == 0) { if ((region_iter->alignment != align) && - BLI_rcti_isect(®ion_iter->winrct, ®ion->winrct, NULL)) { + BLI_rcti_isect(®ion_iter->winrct, ®ion->winrct, nullptr)) { /* Left overlapping right or vice-versa, forbid this! */ region->flag |= RGN_FLAG_TOO_SMALL; return; @@ -1269,7 +1257,7 @@ static void region_rect_recursive( { rcti *remainder_prev = remainder; - if (region == NULL) { + if (region == nullptr) { return; } @@ -1294,7 +1282,7 @@ static void region_rect_recursive( /* clear state flags first */ region->flag &= ~(RGN_FLAG_TOO_SMALL | RGN_FLAG_SIZE_CLAMP_X | RGN_FLAG_SIZE_CLAMP_Y); /* user errors */ - if ((region->next == NULL) && !ELEM(alignment, RGN_ALIGN_QSPLIT, RGN_ALIGN_FLOAT)) { + if ((region->next == nullptr) && !ELEM(alignment, RGN_ALIGN_QSPLIT, RGN_ALIGN_FLOAT)) { alignment = RGN_ALIGN_NONE; } @@ -1368,12 +1356,12 @@ static void region_rect_recursive( } /* We need to use a test that won't have been previously clamped. */ - rcti winrct_test = { - .xmin = region->winrct.xmin, - .ymin = region->winrct.ymin, - .xmax = region->winrct.xmin + size_min[0], - .ymax = region->winrct.ymin + size_min[1], - }; + rcti winrct_test{}; + winrct_test.xmin = region->winrct.xmin; + winrct_test.ymin = region->winrct.ymin; + winrct_test.xmax = region->winrct.xmin + size_min[0]; + winrct_test.ymax = region->winrct.ymin + size_min[1]; + BLI_rcti_isect(&winrct_test, &overlap_remainder_margin, &winrct_test); if (BLI_rcti_size_x(&winrct_test) < size_min[0] || BLI_rcti_size_y(&winrct_test) < size_min[1]) { @@ -1655,7 +1643,7 @@ static bool event_in_markers_region(const ARegion *region, const wmEvent *event) } /** - * \param region: Region, may be NULL when adding handlers for \a area. + * \param region: Region, may be nullptr when adding handlers for \a area. */ static void ed_default_handlers( wmWindowManager *wm, ScrArea *area, ARegion *region, ListBase *handlers, int flag) @@ -1680,9 +1668,11 @@ static void ed_default_handlers( if (region) { /* Anything else is confusing, only allow this. */ BLI_assert(®ion->handlers == handlers); - if (region->gizmo_map == NULL) { - region->gizmo_map = WM_gizmomap_new_from_type( - &(const struct wmGizmoMapType_Params){area->spacetype, region->type->regionid}); + if (region->gizmo_map == nullptr) { + wmGizmoMapType_Params params{}; + params.spaceid = area->spacetype; + params.regionid = region->type->regionid; + region->gizmo_map = WM_gizmomap_new_from_type(¶ms); } WM_gizmomap_add_handlers(region, region->gizmo_map); } @@ -1866,7 +1856,8 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar /* region rect sizes */ rcti rect = area->totrct; rcti overlap_rect = rect; - region_rect_recursive(area, area->regionbase.first, &rect, &overlap_rect, 0); + region_rect_recursive( + area, static_cast(area->regionbase.first), &rect, &overlap_rect, 0); /* Dynamically sized regions may have changed region sizes, so we have to force azone update. */ area_azone_init(win, screen, area); @@ -1887,7 +1878,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar area->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE; } -bool ED_area_has_shared_border(struct ScrArea *a, struct ScrArea *b) +bool ED_area_has_shared_border(ScrArea *a, ScrArea *b) { return area_getorientation(a, b) != -1; } @@ -1909,7 +1900,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) /* Set type-definitions. */ area->type = BKE_spacetype_from_id(area->spacetype); - if (area->type == NULL) { + if (area->type == nullptr) { area->spacetype = SPACE_VIEW3D; area->type = BKE_spacetype_from_id(area->spacetype); } @@ -1924,11 +1915,12 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) /* region rect sizes */ rcti rect = area->totrct; rcti overlap_rect = rect; - region_rect_recursive(area, area->regionbase.first, &rect, &overlap_rect, 0); + region_rect_recursive( + area, static_cast(area->regionbase.first), &rect, &overlap_rect, 0); area->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE; /* default area handlers */ - ed_default_handlers(wm, area, NULL, &area->handlers, area->type->keymapflag); + ed_default_handlers(wm, area, nullptr, &area->handlers, area->type->keymapflag); /* checks spacedata, adds own handlers */ if (area->type->init) { area->type->init(wm, area); @@ -1951,7 +1943,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) } else { /* prevent uiblocks to run */ - UI_blocklist_free(NULL, region); + UI_blocklist_free(nullptr, region); } /* Some AZones use View2D data which is only updated in region init, so call that first! */ @@ -1965,7 +1957,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) area->flag |= AREA_FLAG_ACTIVE_TOOL_UPDATE; } else { - area->runtime.tool = NULL; + area->runtime.tool = nullptr; area->runtime.is_tool_set = true; } } @@ -1976,7 +1968,7 @@ static void area_offscreen_init(ScrArea *area) area->flag |= AREA_FLAG_OFFSCREEN; area->type = BKE_spacetype_from_id(area->spacetype); - if (area->type == NULL) { + if (area->type == nullptr) { area->spacetype = SPACE_VIEW3D; area->type = BKE_spacetype_from_id(area->spacetype); } @@ -1988,7 +1980,7 @@ static void area_offscreen_init(ScrArea *area) ScrArea *ED_area_offscreen_create(wmWindow *win, eSpace_Type space_type) { - ScrArea *area = MEM_callocN(sizeof(*area), __func__); + ScrArea *area = MEM_cnew(__func__); area->spacetype = space_type; screen_area_spacelink_add(WM_window_get_active_scene(win), area, space_type); @@ -2008,14 +2000,14 @@ static void area_offscreen_exit(wmWindowManager *wm, wmWindow *win, ScrArea *are region->type->exit(wm, region); } - WM_event_modal_handler_region_replace(win, region, NULL); + WM_event_modal_handler_region_replace(win, region, nullptr); WM_draw_region_free(region, true); MEM_SAFE_FREE(region->headerstr); if (region->regiontimer) { WM_event_remove_timer(wm, win, region->regiontimer); - region->regiontimer = NULL; + region->regiontimer = nullptr; } if (wm->message_bus) { @@ -2023,7 +2015,7 @@ static void area_offscreen_exit(wmWindowManager *wm, wmWindow *win, ScrArea *are } } - WM_event_modal_handler_area_replace(win, area, NULL); + WM_event_modal_handler_area_replace(win, area, nullptr); } void ED_area_offscreen_free(wmWindowManager *wm, wmWindow *win, ScrArea *area) @@ -2060,8 +2052,8 @@ void ED_region_floating_init(ARegion *region) void ED_region_cursor_set(wmWindow *win, ScrArea *area, ARegion *region) { - if (region != NULL) { - if ((region->gizmo_map != NULL) && WM_gizmomap_cursor_set(region->gizmo_map, win)) { + if (region != nullptr) { + if ((region->gizmo_map != nullptr) && WM_gizmomap_cursor_set(region->gizmo_map, win)) { return; } if (area && region->type && region->type->cursor) { @@ -2084,7 +2076,7 @@ void ED_region_visibility_change_update(bContext *C, ScrArea *area, ARegion *reg /* Needed to close any open pop-overs which would otherwise remain open, * crashing on attempting to refresh. See: #93410. * - * When #ED_area_init frees buttons via #UI_blocklist_free a NULL context + * When #ED_area_init frees buttons via #UI_blocklist_free a nullptr context * is passed, causing the free not to remove menus or their handlers. */ UI_region_free_active_but_all(C, region); } @@ -2180,7 +2172,7 @@ struct RegionTypeAlignInfo { } by_type[RGN_TYPE_NUM]; }; -static void region_align_info_from_area(ScrArea *area, struct RegionTypeAlignInfo *r_align_info) +static void region_align_info_from_area(ScrArea *area, RegionTypeAlignInfo *r_align_info) { for (int index = 0; index < RGN_TYPE_NUM; index++) { r_align_info->by_type[index].alignment = -1; @@ -2205,7 +2197,7 @@ static void region_align_info_from_area(ScrArea *area, struct RegionTypeAlignInf * The tool-header is used when visible, otherwise the header is used. */ static short region_alignment_from_header_and_tool_header_state( - const struct RegionTypeAlignInfo *region_align_info, const short fallback) + const RegionTypeAlignInfo *region_align_info, const short fallback) { const short header_alignment = region_align_info->by_type[RGN_TYPE_HEADER].alignment; const short tool_header_alignment = region_align_info->by_type[RGN_TYPE_TOOL_HEADER].alignment; @@ -2250,10 +2242,9 @@ static short region_alignment_from_header_and_tool_header_state( * and use this to set alignment for the headers in the destination area. * - Header & tool-header/footer may be on opposite sides, this is preserved when syncing. */ -static void region_align_info_to_area_for_headers( - const struct RegionTypeAlignInfo *region_align_info_src, - const struct RegionTypeAlignInfo *region_align_info_dst, - ARegion *region_by_type[RGN_TYPE_NUM]) +static void region_align_info_to_area_for_headers(const RegionTypeAlignInfo *region_align_info_src, + const RegionTypeAlignInfo *region_align_info_dst, + ARegion *region_by_type[RGN_TYPE_NUM]) { /* Abbreviate access. */ const short header_alignment_src = region_align_info_src->by_type[RGN_TYPE_HEADER].alignment; @@ -2342,7 +2333,7 @@ static void region_align_info_to_area_for_headers( /* Finally apply synchronized flags. */ if (header_alignment_sync != -1) { ARegion *region = region_by_type[RGN_TYPE_HEADER]; - if (region != NULL) { + if (region != nullptr) { region->alignment = RGN_ALIGN_ENUM_FROM_MASK(header_alignment_sync) | RGN_ALIGN_FLAG_FROM_MASK(region->alignment); } @@ -2350,7 +2341,7 @@ static void region_align_info_to_area_for_headers( if (tool_header_alignment_sync != -1) { ARegion *region = region_by_type[RGN_TYPE_TOOL_HEADER]; - if (region != NULL) { + if (region != nullptr) { region->alignment = RGN_ALIGN_ENUM_FROM_MASK(tool_header_alignment_sync) | RGN_ALIGN_FLAG_FROM_MASK(region->alignment); } @@ -2358,7 +2349,7 @@ static void region_align_info_to_area_for_headers( if (footer_alignment_sync != -1) { ARegion *region = region_by_type[RGN_TYPE_FOOTER]; - if (region != NULL) { + if (region != nullptr) { region->alignment = RGN_ALIGN_ENUM_FROM_MASK(footer_alignment_sync) | RGN_ALIGN_FLAG_FROM_MASK(region->alignment); } @@ -2366,9 +2357,9 @@ static void region_align_info_to_area_for_headers( } static void region_align_info_to_area( - ScrArea *area, const struct RegionTypeAlignInfo region_align_info_src[RGN_TYPE_NUM]) + ScrArea *area, const RegionTypeAlignInfo region_align_info_src[RGN_TYPE_NUM]) { - ARegion *region_by_type[RGN_TYPE_NUM] = {NULL}; + ARegion *region_by_type[RGN_TYPE_NUM] = {nullptr}; LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { const int index = region->regiontype; if ((uint)index < RGN_TYPE_NUM) { @@ -2376,11 +2367,11 @@ static void region_align_info_to_area( } } - struct RegionTypeAlignInfo region_align_info_dst; + RegionTypeAlignInfo region_align_info_dst; region_align_info_from_area(area, ®ion_align_info_dst); - if ((region_by_type[RGN_TYPE_HEADER] != NULL) || - (region_by_type[RGN_TYPE_TOOL_HEADER] != NULL)) { + if ((region_by_type[RGN_TYPE_HEADER] != nullptr) || + (region_by_type[RGN_TYPE_TOOL_HEADER] != nullptr)) { region_align_info_to_area_for_headers( region_align_info_src, ®ion_align_info_dst, region_by_type); } @@ -2394,7 +2385,7 @@ static void region_align_info_to_area( void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2) { - ScrArea *tmp = MEM_callocN(sizeof(ScrArea), "addscrarea"); + ScrArea *tmp = MEM_cnew(__func__); wmWindow *win = CTX_wm_window(C); ED_area_exit(C, sa1); @@ -2407,7 +2398,7 @@ void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2) ED_area_init(CTX_wm_manager(C), win, sa2); BKE_screen_area_free(tmp); - MEM_freeN(tmp); + MEM_delete(tmp); /* tell WM to refresh, cursor types etc */ WM_event_add_mousemove(win); @@ -2423,9 +2414,9 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi wmWindow *win = CTX_wm_window(C); if (area->spacetype != type) { - SpaceLink *slold = area->spacedata.first; + SpaceLink *slold = static_cast(area->spacedata.first); /* store area->type->exit callback */ - void *area_exit = area->type ? area->type->exit : NULL; + void (*area_exit)(wmWindowManager *, ScrArea *) = area->type ? area->type->exit : nullptr; /* When the user switches between space-types from the type-selector, * changing the header-type is jarring (especially when using Ctrl-MouseWheel). * @@ -2438,8 +2429,8 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi */ bool sync_header_alignment = false; - struct RegionTypeAlignInfo region_align_info[RGN_TYPE_NUM]; - if ((slold != NULL) && (slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) { + RegionTypeAlignInfo region_align_info[RGN_TYPE_NUM]; + if ((slold != nullptr) && (slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) { region_align_info_from_area(area, region_align_info); sync_header_alignment = true; } @@ -2447,7 +2438,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi /* in some cases (opening temp space) we don't want to * call area exit callback, so we temporarily unset it */ if (skip_region_exit && area->type) { - area->type->exit = NULL; + area->type->exit = nullptr; } ED_area_exit(C, area); @@ -2467,7 +2458,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi * (e.g. with properties editor) until space-data is properly created */ /* check previously stored space */ - SpaceLink *sl = NULL; + SpaceLink *sl = nullptr; LISTBASE_FOREACH (SpaceLink *, sl_iter, &area->spacedata) { if (sl_iter->spacetype == type) { sl = sl_iter; @@ -2480,9 +2471,9 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi st->free(sl); BLI_freelinkN(&area->spacedata, sl); if (slold == sl) { - slold = NULL; + slold = nullptr; } - sl = NULL; + sl = nullptr; } if (sl) { @@ -2538,7 +2529,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi static SpaceLink *area_get_prevspace(ScrArea *area) { - SpaceLink *sl = area->spacedata.first; + SpaceLink *sl = static_cast(area->spacedata.first); /* First toggle to the next temporary space in the list. */ for (SpaceLink *sl_iter = sl->next; sl_iter; sl_iter = sl_iter->next) { @@ -2560,8 +2551,8 @@ static SpaceLink *area_get_prevspace(ScrArea *area) void ED_area_prevspace(bContext *C, ScrArea *area) { - SpaceLink *sl = area->spacedata.first; - SpaceLink *prevspace = sl ? area_get_prevspace(area) : NULL; + SpaceLink *sl = static_cast(area->spacedata.first); + SpaceLink *prevspace = sl ? area_get_prevspace(area) : nullptr; if (prevspace) { ED_area_newspace(C, area, prevspace->spacetype, false); @@ -2668,7 +2659,7 @@ BLI_INLINE bool streq_array_any(const char *s, const char *arr[]) * in which case a panel with the type of \a pt will be created. * \param unique_panel_str: A unique identifier for the name of the \a uiBlock associated with the * panel. Used when the panel is an instanced panel so a unique identifier is needed to find the - * correct old \a uiBlock, and NULL otherwise. + * correct old \a uiBlock, and nullptr otherwise. */ static void ed_panel_draw(const bContext *C, ARegion *region, @@ -2696,7 +2687,7 @@ static void ed_panel_draw(const bContext *C, bool open; panel = UI_panel_begin(region, lb, block, pt, panel, &open); - const bool search_filter_active = search_filter != NULL && search_filter[0] != '\0'; + const bool search_filter_active = search_filter != nullptr && search_filter[0] != '\0'; /* bad fixed values */ int xco, yco, h = 0; @@ -2720,7 +2711,7 @@ static void ed_panel_draw(const bContext *C, UI_block_apply_search_filter(block, search_filter); UI_block_layout_resolve(block, &xco, &yco); UI_block_translate(block, headerend - xco, 0); - panel->layout = NULL; + panel->layout = nullptr; } if (pt->draw_header && !(pt->flag & PANEL_TYPE_NO_HEADER)) { @@ -2751,7 +2742,7 @@ static void ed_panel_draw(const bContext *C, UI_block_apply_search_filter(block, search_filter); UI_block_layout_resolve(block, &xco, &yco); panel->labelofs = xco - labelx; - panel->layout = NULL; + panel->layout = nullptr; } else { panel->labelofs = 0; @@ -2787,7 +2778,7 @@ static void ed_panel_draw(const bContext *C, UI_block_apply_search_filter(block, search_filter); UI_block_layout_resolve(block, &xco, &yco); - panel->layout = NULL; + panel->layout = nullptr; if (yco != 0) { h = -yco + 2 * style->panelspace; @@ -2799,7 +2790,7 @@ static void ed_panel_draw(const bContext *C, /* Draw child panels. */ if (open || search_filter_active) { LISTBASE_FOREACH (LinkData *, link, &pt->children) { - PanelType *child_pt = link->data; + PanelType *child_pt = static_cast(link->data); Panel *child_panel = UI_panel_find_by_type(&panel->children, child_pt); if (child_pt->draw && (!child_pt->poll || child_pt->poll(C, child_pt))) { @@ -2840,7 +2831,7 @@ static bool panel_add_check(const bContext *C, } /* Verify context. */ - if (contexts != NULL && panel_type->context[0]) { + if (contexts != nullptr && panel_type->context[0]) { if (!streq_array_any(panel_type->context, contexts)) { return false; } @@ -2878,7 +2869,7 @@ static const char *region_panels_collect_categories(ARegion *region, /* gather unique categories */ for (LinkNode *pt_link = panel_types_stack; pt_link; pt_link = pt_link->next) { - PanelType *pt = pt_link->link; + PanelType *pt = static_cast(pt_link->link); if (pt->category[0]) { if (!UI_panel_category_find(region, pt->category)) { UI_panel_category_add(region, pt->category); @@ -2891,7 +2882,7 @@ static const char *region_panels_collect_categories(ARegion *region, } *use_category_tabs = false; - return NULL; + return nullptr; } static int panel_draw_width_from_max_width_get(const ARegion *region, @@ -2912,21 +2903,22 @@ void ED_region_panels_layout_ex(const bContext *C, { /* collect panels to draw */ WorkSpace *workspace = CTX_wm_workspace(C); - LinkNode *panel_types_stack = NULL; + LinkNode *panel_types_stack = nullptr; LISTBASE_FOREACH_BACKWARD (PanelType *, pt, paneltypes) { if (panel_add_check(C, workspace, contexts, category_override, pt)) { BLI_linklist_prepend_alloca(&panel_types_stack, pt); } } - region->runtime.category = NULL; + region->runtime.category = nullptr; ScrArea *area = CTX_wm_area(C); View2D *v2d = ®ion->v2d; - bool use_category_tabs = (category_override == NULL) && region_uses_category_tabs(area, region); + bool use_category_tabs = (category_override == nullptr) && + region_uses_category_tabs(area, region); /* offset panels for small vertical tab area */ - const char *category = NULL; + const char *category = nullptr; const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH; int margin_x = 0; const bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE; @@ -2961,7 +2953,7 @@ void ED_region_panels_layout_ex(const bContext *C, bool has_instanced_panel = false; for (LinkNode *pt_link = panel_types_stack; pt_link; pt_link = pt_link->next) { - PanelType *pt = pt_link->link; + PanelType *pt = static_cast(pt_link->link); if (pt->flag & PANEL_TYPE_INSTANCED) { has_instanced_panel = true; @@ -2970,7 +2962,7 @@ void ED_region_panels_layout_ex(const bContext *C, Panel *panel = UI_panel_find_by_type(®ion->panels, pt); if (use_category_tabs && pt->category[0] && !STREQ(category, pt->category)) { - if ((panel == NULL) || ((panel->flag & PNL_PIN) == 0)) { + if ((panel == nullptr) || ((panel->flag & PNL_PIN) == 0)) { continue; } } @@ -2981,13 +2973,13 @@ void ED_region_panels_layout_ex(const bContext *C, update_tot_size = false; } - ed_panel_draw(C, region, ®ion->panels, pt, panel, width, em, NULL, search_filter); + ed_panel_draw(C, region, ®ion->panels, pt, panel, width, em, nullptr, search_filter); } /* Draw "poly-instantiated" panels that don't have a 1 to 1 correspondence with their types. */ if (has_instanced_panel) { LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { - if (panel->type == NULL) { + if (panel->type == nullptr) { continue; /* Some panels don't have a type. */ } if (!(panel->type->flag & PANEL_TYPE_INSTANCED)) { @@ -3029,11 +3021,11 @@ void ED_region_panels_layout_ex(const bContext *C, /* XXX, only single panel support at the moment. * Can't use x/y values calculated above because they're not using the real height of panels, * instead they calculate offsets for the next panel to start drawing. */ - Panel *panel = region->panels.last; - if (panel != NULL) { + Panel *panel = static_cast(region->panels.last); + if (panel != nullptr) { const int size_dyn[2] = { - UI_UNIT_X * (UI_panel_is_closed(panel) ? 8 : 14) / UI_DPI_FAC, - UI_panel_size_y(panel) / UI_DPI_FAC, + int(UI_UNIT_X * (UI_panel_is_closed(panel) ? 8 : 14) / UI_DPI_FAC), + int(UI_panel_size_y(panel) / UI_DPI_FAC), }; /* region size is layout based and needs to be updated */ if ((region->sizex != size_dyn[0]) || (region->sizey != size_dyn[1])) { @@ -3074,7 +3066,7 @@ void ED_region_panels_layout_ex(const bContext *C, void ED_region_panels_layout(const bContext *C, ARegion *region) { - ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, NULL, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, nullptr, nullptr); } void ED_region_panels_draw(const bContext *C, ARegion *region) @@ -3120,13 +3112,13 @@ void ED_region_panels_draw(const bContext *C, ARegion *region) /* Don't always show scrollbars for transparent regions as it's distracting. */ use_full_hide = true; } - UI_view2d_scrollers_draw_ex(v2d, use_mask ? &mask : NULL, use_full_hide); + UI_view2d_scrollers_draw_ex(v2d, use_mask ? &mask : nullptr, use_full_hide); } void ED_region_panels_ex(const bContext *C, ARegion *region, const char *contexts[]) { /* TODO: remove? */ - ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts, nullptr); ED_region_panels_draw(C, region); } @@ -3149,7 +3141,7 @@ void ED_region_panels_init(wmWindowManager *wm, ARegion *region) * Check whether any of the buttons generated by the \a panel_type's * layout callbacks match the \a search_filter. * - * \param panel: If non-NULL, use this instead of adding a new panel for the \a panel_type. + * \param panel: If non-null, use this instead of adding a new panel for the \a panel_type. */ static bool panel_property_search(const bContext *C, ARegion *region, @@ -3166,24 +3158,24 @@ static bool panel_property_search(const bContext *C, return false; } - if (panel == NULL) { + if (panel == nullptr) { bool open; /* Dummy variable. */ panel = UI_panel_begin(region, ®ion->panels, block, panel_type, panel, &open); } /* Build the layouts. Because they are only used for search, * they don't need any of the proper style or layout information. */ - if (panel->type->draw_header_preset != NULL) { + if (panel->type->draw_header_preset != nullptr) { panel->layout = UI_block_layout( block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, 0, 0, 0, 0, 0, style); panel_type->draw_header_preset(C, panel); } - if (panel->type->draw_header != NULL) { + if (panel->type->draw_header != nullptr) { panel->layout = UI_block_layout( block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, 0, 0, 0, 0, 0, style); panel_type->draw_header(C, panel); } - if (LIKELY(panel->type->draw != NULL)) { + if (LIKELY(panel->type->draw != nullptr)) { panel->layout = UI_block_layout( block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, 0, 0, 0, style); panel_type->draw(C, panel); @@ -3198,7 +3190,7 @@ static bool panel_property_search(const bContext *C, } LISTBASE_FOREACH (LinkData *, link, &panel_type->children) { - PanelType *panel_type_child = link->data; + PanelType *panel_type_child = static_cast(link->data); if (!panel_type_child->poll || panel_type_child->poll(C, panel_type_child)) { /* Search for the existing child panel here because it might be an instanced * child panel with a custom data field that will be needed to build the layout. */ @@ -3223,15 +3215,16 @@ bool ED_region_property_search(const bContext *C, const uiStyle *style = UI_style_get_dpi(); const char *search_filter = ED_area_region_search_filter_get(area, region); - LinkNode *panel_types_stack = NULL; + LinkNode *panel_types_stack = nullptr; LISTBASE_FOREACH_BACKWARD (PanelType *, pt, paneltypes) { if (panel_add_check(C, workspace, contexts, category_override, pt)) { BLI_linklist_prepend_alloca(&panel_types_stack, pt); } } - const char *category = NULL; - bool use_category_tabs = (category_override == NULL) && region_uses_category_tabs(area, region); + const char *category = nullptr; + bool use_category_tabs = (category_override == nullptr) && + region_uses_category_tabs(area, region); if (use_category_tabs) { category = region_panels_collect_categories(region, panel_types_stack, &use_category_tabs); } @@ -3240,7 +3233,7 @@ bool ED_region_property_search(const bContext *C, bool has_result = true; bool has_instanced_panel = false; for (LinkNode *pt_link = panel_types_stack; pt_link; pt_link = pt_link->next) { - PanelType *panel_type = pt_link->link; + PanelType *panel_type = static_cast(pt_link->link); /* Note that these checks are duplicated from #ED_region_panels_layout_ex. */ if (panel_type->flag & PANEL_TYPE_INSTANCED) { has_instanced_panel = true; @@ -3255,7 +3248,7 @@ bool ED_region_property_search(const bContext *C, /* We start property search with an empty panel list, so there's * no point in trying to find an existing panel with this type. */ - has_result = panel_property_search(C, region, style, NULL, panel_type, search_filter); + has_result = panel_property_search(C, region, style, nullptr, panel_type, search_filter); if (has_result) { break; } @@ -3265,7 +3258,7 @@ bool ED_region_property_search(const bContext *C, if (!has_result && has_instanced_panel) { LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { /* Note that these checks are duplicated from #ED_region_panels_layout_ex. */ - if (panel->type == NULL || !(panel->type->flag & PANEL_TYPE_INSTANCED)) { + if (panel->type == nullptr || !(panel->type->flag & PANEL_TYPE_INSTANCED)) { continue; } if (use_category_tabs) { @@ -3296,7 +3289,7 @@ void ED_region_header_layout(const bContext *C, ARegion *region) /* Height of buttons and scaling needed to achieve it. */ const int buttony = min_ii(UI_UNIT_Y, region->winy - 2 * UI_DPI_FAC); - const float buttony_scale = buttony / (float)UI_UNIT_Y; + const float buttony_scale = buttony / float(UI_UNIT_Y); /* Vertically center buttons. */ int xco = UI_HEADER_OFFSET; @@ -3326,7 +3319,7 @@ void ED_region_header_layout(const bContext *C, ARegion *region) uiLayoutSetScaleY(layout, buttony_scale); } - Header header = {NULL}; + Header header = {nullptr}; if (ht->draw) { header.type = ht; header.layout = layout; @@ -3409,7 +3402,7 @@ void ED_region_header_init(ARegion *region) int ED_area_headersize(void) { /* Accommodate widget and padding. */ - return U.widget_unit + (int)(UI_DPI_FAC * HEADER_PADDING_Y); + return U.widget_unit + int(UI_DPI_FAC * HEADER_PADDING_Y); } int ED_area_footersize(void) @@ -3435,7 +3428,7 @@ int ED_area_global_max_size_y(const ScrArea *area) bool ED_area_is_global(const ScrArea *area) { - return area->global != NULL; + return area->global != nullptr; } ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int xy[2]) @@ -3443,7 +3436,7 @@ ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int x bScreen *screen = CTX_wm_screen(C); wmWindow *win = CTX_wm_window(C); - ScrArea *area = NULL; + ScrArea *area = nullptr; if (win->parent) { /* If active window is a child, check itself first. */ @@ -3471,10 +3464,10 @@ ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int x ScrArea *ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen) { - ScrArea *global_area = win->global_areas.areabase.first; + ScrArea *global_area = static_cast(win->global_areas.areabase.first); if (!global_area) { - return screen->areabase.first; + return static_cast(screen->areabase.first); } if ((global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) == 0) { return global_area; @@ -3484,7 +3477,7 @@ ScrArea *ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen) } ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area) { - if (area->global == NULL) { + if (area->global == nullptr) { return area->next; } @@ -3494,7 +3487,7 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area) } } /* No visible next global area found, start iterating over layout areas. */ - return screen->areabase.first; + return static_cast(screen->areabase.first); } int ED_region_global_size_y(void) @@ -3583,13 +3576,13 @@ void ED_region_info_draw(ARegion *region, float fill_color[4], const bool full_redraw) { - const char *text_array[2] = {text, NULL}; + const char *text_array[2] = {text, nullptr}; ED_region_info_draw_multiline(region, text_array, fill_color, full_redraw); } -typedef struct MetadataPanelDrawContext { +struct MetadataPanelDrawContext { uiLayout *layout; -} MetadataPanelDrawContext; +}; static void metadata_panel_draw_field(const char *field, const char *value, void *ctx_v) { @@ -3660,7 +3653,7 @@ void ED_region_grid_draw(ARegion *region, float zoomx, float zoomy, float x0, fl immBegin(GPU_PRIM_LINES, 4 * count_fine + 4 * count_large); float theme_color[3]; - UI_GetThemeColorShade3fv(TH_GRID, (int)(20.0f * (1.0f - blendfac)), theme_color); + UI_GetThemeColorShade3fv(TH_GRID, int(20.0f * (1.0f - blendfac)), theme_color); float fac = 0.0f; /* the fine resolution level */ @@ -3715,7 +3708,7 @@ static void region_visible_rect_calc(ARegion *region, rcti *rect) /* check if a region overlaps with the current one */ for (; region_iter; region_iter = region_iter->next) { if (region != region_iter && region_iter->overlap) { - if (BLI_rcti_isect(rect, ®ion_iter->winrct, NULL)) { + if (BLI_rcti_isect(rect, ®ion_iter->winrct, nullptr)) { int alignment = RGN_ALIGN_ENUM_FROM_MASK(region_iter->alignment); if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { @@ -3814,8 +3807,8 @@ void ED_region_cache_draw_cached_segments( immUniformColor4ub(128, 128, 255, 128); for (int a = 0; a < num_segments; a++) { - float x1 = (float)(points[a * 2] - sfra) / (efra - sfra + 1) * region->winx; - float x2 = (float)(points[a * 2 + 1] - sfra + 1) / (efra - sfra + 1) * region->winx; + float x1 = float(points[a * 2] - sfra) / (efra - sfra + 1) * region->winx; + float x2 = float(points[a * 2 + 1] - sfra + 1) / (efra - sfra + 1) * region->winx; immRecti(pos, x1, region_bottom, x2, region_bottom + 8 * UI_DPI_FAC); /* TODO(merwin): use primitive restart to draw multiple rects more efficiently */ @@ -3829,9 +3822,9 @@ void ED_region_message_subscribe(wmRegionMessageSubscribeParams *params) { ARegion *region = params->region; const bContext *C = params->context; - struct wmMsgBus *mbus = params->message_bus; + wmMsgBus *mbus = params->message_bus; - if (region->gizmo_map != NULL) { + if (region->gizmo_map != nullptr) { WM_gizmomap_message_subscribe(C, region->gizmo_map, region, mbus); } @@ -3839,7 +3832,7 @@ void ED_region_message_subscribe(wmRegionMessageSubscribeParams *params) UI_region_message_subscribe(region, mbus); } - if (region->type->message_subscribe != NULL) { + if (region->type->message_subscribe != nullptr) { region->type->message_subscribe(params); } } @@ -3848,7 +3841,7 @@ int ED_region_snap_size_test(const ARegion *region) { /* Use a larger value because toggling scrollbars can jump in size. */ const int snap_match_threshold = 16; - if (region->type->snap_size != NULL) { + if (region->type->snap_size != nullptr) { return ((((region->sizex - region->type->snap_size(region, region->sizex, 0)) <= snap_match_threshold) << 0) | @@ -3862,7 +3855,7 @@ int ED_region_snap_size_test(const ARegion *region) bool ED_region_snap_size_apply(ARegion *region, int snap_flag) { bool changed = false; - if (region->type->snap_size != NULL) { + if (region->type->snap_size != nullptr) { if (snap_flag & (1 << 0)) { short snap_size = region->type->snap_size(region, region->sizex, 0); if (snap_size != region->sizex) { diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index feda68a51a7..d1fe516b406 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -11,6 +11,10 @@ struct Main; struct bContext; struct bContextDataResult; +#ifdef __cplusplus +extern "C" { +#endif + /* internal exports only */ typedef enum eScreenDir { @@ -50,7 +54,7 @@ typedef enum eScreenAxis { */ #define BORDERPADDING ((2.0f * U.dpi_fac) + U.pixelsize) -/* area.c */ +/* area.cc */ /** * We swap spaces for full-screen to keep all allocated data area vertices were set. @@ -182,3 +186,7 @@ void SCREEN_OT_screenshot_area(struct wmOperatorType *ot); /* workspace_layout_edit.c */ bool workspace_layout_set_poll(const struct WorkSpaceLayout *layout); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.cc b/source/blender/editors/sculpt_paint/paint_image_proj.cc index 1800f8f862b..04ea1d3f91f 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.cc +++ b/source/blender/editors/sculpt_paint/paint_image_proj.cc @@ -414,17 +414,17 @@ struct ProjPaintState { const float (*vert_positions_eval)[3]; const float (*vert_normals)[3]; - const MEdge *medge_eval; - const MPoly *mpoly_eval; + blender::Span edges_eval; + blender::Span polys_eval; + blender::Span loops_eval; const bool *select_poly_eval; const int *material_indices; - const MLoop *mloop_eval; const MLoopTri *mlooptri_eval; const float (*mloopuv_stencil_eval)[2]; /** - * \note These UV layers are aligned to \a mpoly_eval + * \note These UV layers are aligned to \a polys_eval * but each pointer references the start of the layer, * so a loop indirection is needed as well. */ @@ -509,8 +509,8 @@ struct VertSeam { * \{ */ #define PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) \ - int(ps->mloop_eval[lt->tri[0]].v), int(ps->mloop_eval[lt->tri[1]].v), \ - int(ps->mloop_eval[lt->tri[2]].v), + int(ps->loops_eval[lt->tri[0]].v), int(ps->loops_eval[lt->tri[1]].v), \ + int(ps->loops_eval[lt->tri[2]].v), #define PS_LOOPTRI_AS_UV_3(uvlayer, lt) \ uvlayer[lt->poly][lt->tri[0]], uvlayer[lt->poly][lt->tri[1]], uvlayer[lt->poly][lt->tri[2]], @@ -675,9 +675,9 @@ static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], f const int tri_index = POINTER_AS_INT(node->link); const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; const float *vtri_ss[3] = { - ps->screenCoords[ps->mloop_eval[lt->tri[0]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[1]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[2]].v], + ps->screenCoords[ps->loops_eval[lt->tri[0]].v], + ps->screenCoords[ps->loops_eval[lt->tri[1]].v], + ps->screenCoords[ps->loops_eval[lt->tri[2]].v], }; if (isect_point_tri_v2(pt, UNPACK3(vtri_ss))) { @@ -917,17 +917,17 @@ static bool project_bucket_point_occluded(const ProjPaintState *ps, if (orig_face != tri_index) { const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; const float *vtri_ss[3] = { - ps->screenCoords[ps->mloop_eval[lt->tri[0]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[1]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[2]].v], + ps->screenCoords[ps->loops_eval[lt->tri[0]].v], + ps->screenCoords[ps->loops_eval[lt->tri[1]].v], + ps->screenCoords[ps->loops_eval[lt->tri[2]].v], }; float w[3]; if (do_clip) { const float *vtri_co[3] = { - ps->vert_positions_eval[ps->mloop_eval[lt->tri[0]].v], - ps->vert_positions_eval[ps->mloop_eval[lt->tri[1]].v], - ps->vert_positions_eval[ps->mloop_eval[lt->tri[2]].v], + ps->vert_positions_eval[ps->loops_eval[lt->tri[0]].v], + ps->vert_positions_eval[ps->loops_eval[lt->tri[1]].v], + ps->vert_positions_eval[ps->loops_eval[lt->tri[2]].v], }; isect_ret = project_paint_occlude_ptv_clip( pixelScreenCo, UNPACK3(vtri_ss), UNPACK3(vtri_co), w, ps->is_ortho, ps->rv3d); @@ -1144,8 +1144,8 @@ static bool check_seam(const ProjPaintState *ps, const MLoopTri *orig_lt = &ps->mlooptri_eval[orig_face]; const float *orig_lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, orig_lt)}; /* vert indices from face vert order indices */ - const uint i1 = ps->mloop_eval[orig_lt->tri[orig_i1_fidx]].v; - const uint i2 = ps->mloop_eval[orig_lt->tri[orig_i2_fidx]].v; + const uint i1 = ps->loops_eval[orig_lt->tri[orig_i1_fidx]].v; + const uint i2 = ps->loops_eval[orig_lt->tri[orig_i2_fidx]].v; LinkNode *node; /* index in face */ int i1_fidx = -1, i2_fidx = -1; @@ -1330,8 +1330,8 @@ static void uv_image_outset(const ProjPaintState *ps, fidx[1] = (fidx[0] == 2) ? 0 : fidx[0] + 1; - vert[0] = ps->mloop_eval[loop_index].v; - vert[1] = ps->mloop_eval[ltri->tri[fidx[1]]].v; + vert[0] = ps->loops_eval[loop_index].v; + vert[1] = ps->loops_eval[ltri->tri[fidx[1]]].v; for (uint i = 0; i < 2; i++) { VertSeam *seam; @@ -1416,7 +1416,7 @@ static void insert_seam_vert_array(const ProjPaintState *ps, copy_v2_v2(vseam[1].uv, lt_tri_uv[fidx[1]]); for (uint i = 0; i < 2; i++) { - uint vert = ps->mloop_eval[lt->tri[fidx[i]]].v; + uint vert = ps->loops_eval[lt->tri[fidx[i]]].v; ListBase *list = &ps->vertSeams[vert]; VertSeam *item = static_cast(list->first); @@ -1455,8 +1455,8 @@ static void project_face_seams_init(const ProjPaintState *ps, } do { - if (init_all || (ps->mloop_eval[lt->tri[fidx[0]]].v == vert_index) || - (ps->mloop_eval[lt->tri[fidx[1]]].v == vert_index)) { + if (init_all || (ps->loops_eval[lt->tri[fidx[0]]].v == vert_index) || + (ps->loops_eval[lt->tri[fidx[1]]].v == vert_index)) { if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM0 << fidx[0] | PROJ_FACE_NOSEAM0 << fidx[0])) == 0) { if (check_seam(ps, tri_index, fidx[0], fidx[1], &other_face, &other_fidx)) { @@ -1503,7 +1503,7 @@ static void project_face_seams_init(const ProjPaintState *ps, continue; } - vert = ps->mloop_eval[lt->tri[fidx[i]]].v; + vert = ps->loops_eval[lt->tri[fidx[i]]].v; for (node = ps->vertFaces[vert]; node; node = node->next) { const int tri = POINTER_AS_INT(node->link); @@ -1721,7 +1721,7 @@ static float project_paint_uvpixel_mask(const ProjPaintState *ps, if (ps->do_mask_normal) { const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; - const MPoly *mp = &ps->mpoly_eval[lt->poly]; + const MPoly *mp = &ps->polys_eval[lt->poly]; float no[3], angle_cos; if (mp->flag & ME_SMOOTH) { @@ -3878,7 +3878,6 @@ static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int di static void proj_paint_state_cavity_init(ProjPaintState *ps) { - const MEdge *me; float *cavities; int a; @@ -3890,14 +3889,15 @@ static void proj_paint_state_cavity_init(ProjPaintState *ps) MEM_mallocN(sizeof(float) * ps->totvert_eval, "ProjectPaint Cavities")); cavities = ps->cavities; - for (a = 0, me = ps->medge_eval; a < ps->totedge_eval; a++, me++) { + for (const int64_t i : ps->edges_eval.index_range()) { + const MEdge &edge = ps->edges_eval[i]; float e[3]; - sub_v3_v3v3(e, ps->vert_positions_eval[me->v1], ps->vert_positions_eval[me->v2]); + sub_v3_v3v3(e, ps->vert_positions_eval[edge.v1], ps->vert_positions_eval[edge.v2]); normalize_v3(e); - add_v3_v3(edges[me->v2], e); - counter[me->v2]++; - sub_v3_v3(edges[me->v1], e); - counter[me->v1]++; + add_v3_v3(edges[edge.v2], e); + counter[edge.v2]++; + sub_v3_v3(edges[edge.v1], e); + counter[edge.v1]++; } for (a = 0; a < ps->totvert_eval; a++) { if (counter[a] > 0) { @@ -4072,11 +4072,9 @@ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *p ps->vert_positions_eval = BKE_mesh_vert_positions(ps->me_eval); ps->vert_normals = BKE_mesh_vertex_normals_ensure(ps->me_eval); - if (ps->do_mask_cavity) { - ps->medge_eval = BKE_mesh_edges(ps->me_eval); - } - ps->mloop_eval = BKE_mesh_loops(ps->me_eval); - ps->mpoly_eval = BKE_mesh_polys(ps->me_eval); + ps->edges_eval = ps->me_eval->edges(); + ps->polys_eval = ps->me_eval->polys(); + ps->loops_eval = ps->me_eval->loops(); ps->select_poly_eval = (const bool *)CustomData_get_layer_named( &ps->me_eval->pdata, CD_PROP_BOOL, ".select_poly"); ps->material_indices = (const int *)CustomData_get_layer_named( @@ -4391,11 +4389,11 @@ static void project_paint_prepare_all_faces(ProjPaintState *ps, if (prev_poly != lt->poly) { int iloop; bool culled = true; - const MPoly *poly = ps->mpoly_eval + lt->poly; + const MPoly *poly = &ps->polys_eval[lt->poly]; int poly_loops = poly->totloop; prev_poly = lt->poly; for (iloop = 0; iloop < poly_loops; iloop++) { - if (!(ps->vertFlags[ps->mloop_eval[poly->loopstart + iloop].v] & PROJ_VERT_CULL)) { + if (!(ps->vertFlags[ps->loops_eval[poly->loopstart + iloop].v] & PROJ_VERT_CULL)) { culled = false; break; } @@ -5954,7 +5952,7 @@ void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int m bool is_multi_view = (ps_handle->ps_views_tot != 1); for (int i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = MEM_cnew("ProjectionPaintState"); + ProjPaintState *ps = MEM_new("ProjectionPaintState"); ps_handle->ps_views[i] = ps; } @@ -6020,8 +6018,7 @@ void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int m fail: for (int i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = ps_handle->ps_views[i]; - MEM_freeN(ps); + MEM_delete(ps_handle->ps_views[i]); } MEM_freeN(ps_handle); return nullptr; diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 79ca5b8ed0a..ce42a2f810f 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1243,54 +1243,62 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex BLI_polyfill_calc(screen_points, tot_screen_points, 0, r_tris); /* Write the front face triangle indices. */ - MPoly *polys = BKE_mesh_polys_for_write(trim_operation->mesh); - MLoop *loops = BKE_mesh_loops_for_write(trim_operation->mesh); - MPoly *mp = polys; - MLoop *ml = loops; - for (int i = 0; i < tot_tris_face; i++, mp++, ml += 3) { - mp->loopstart = int(ml - loops); - mp->totloop = 3; - ml[0].v = r_tris[i][0]; - ml[1].v = r_tris[i][1]; - ml[2].v = r_tris[i][2]; + blender::MutableSpan polys = trim_operation->mesh->polys_for_write(); + blender::MutableSpan loops = trim_operation->mesh->loops_for_write(); + int poly_index = 0; + int loop_index = 0; + for (int i = 0; i < tot_tris_face; i++) { + polys[poly_index].loopstart = loop_index; + polys[poly_index].totloop = 3; + loops[loop_index + 0].v = r_tris[i][0]; + loops[loop_index + 1].v = r_tris[i][1]; + loops[loop_index + 2].v = r_tris[i][2]; + poly_index++; + loop_index += 3; } /* Write the back face triangle indices. */ - for (int i = 0; i < tot_tris_face; i++, mp++, ml += 3) { - mp->loopstart = int(ml - loops); - mp->totloop = 3; - ml[0].v = r_tris[i][0] + tot_screen_points; - ml[1].v = r_tris[i][1] + tot_screen_points; - ml[2].v = r_tris[i][2] + tot_screen_points; + for (int i = 0; i < tot_tris_face; i++) { + polys[poly_index].loopstart = loop_index; + polys[poly_index].totloop = 3; + loops[loop_index + 0].v = r_tris[i][0] + tot_screen_points; + loops[loop_index + 1].v = r_tris[i][1] + tot_screen_points; + loops[loop_index + 2].v = r_tris[i][2] + tot_screen_points; + poly_index++; + loop_index += 3; } MEM_freeN(r_tris); /* Write the indices for the lateral triangles. */ - for (int i = 0; i < tot_screen_points; i++, mp++, ml += 3) { - mp->loopstart = int(ml - loops); - mp->totloop = 3; + for (int i = 0; i < tot_screen_points; i++) { + polys[poly_index].loopstart = loop_index; + polys[poly_index].totloop = 3; int current_index = i; int next_index = current_index + 1; if (next_index >= tot_screen_points) { next_index = 0; } - ml[0].v = next_index + tot_screen_points; - ml[1].v = next_index; - ml[2].v = current_index; + loops[loop_index + 0].v = next_index + tot_screen_points; + loops[loop_index + 1].v = next_index; + loops[loop_index + 2].v = current_index; + poly_index++; + loop_index += 3; } - for (int i = 0; i < tot_screen_points; i++, mp++, ml += 3) { - mp->loopstart = int(ml - loops); - mp->totloop = 3; + for (int i = 0; i < tot_screen_points; i++) { + polys[poly_index].loopstart = loop_index; + polys[poly_index].totloop = 3; int current_index = i; int next_index = current_index + 1; if (next_index >= tot_screen_points) { next_index = 0; } - ml[0].v = current_index; - ml[1].v = current_index + tot_screen_points; - ml[2].v = next_index + tot_screen_points; + loops[loop_index + 0].v = current_index; + loops[loop_index + 1].v = current_index + tot_screen_points; + loops[loop_index + 2].v = next_index + tot_screen_points; + poly_index++; + loop_index += 3; } BKE_mesh_calc_edges(trim_operation->mesh, false, false); diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.cc b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.cc index 1e6a70c8bb2..153a99aa396 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.cc @@ -317,8 +317,8 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); - const MPoly *polys = BKE_mesh_polys(me); - const MLoop *loops = BKE_mesh_loops(me); + const blender::Span polys = me->polys(); + const blender::Span loops = me->loops(); const MDeformVert *dverts = BKE_mesh_deform_verts(me); if (me && dverts && vc.v3d && vc.rv3d && me->vertex_group_names.first) { @@ -438,17 +438,15 @@ void PAINT_OT_weight_sample_group(wmOperatorType *ot) static bool weight_paint_set(Object *ob, float paintweight) { Mesh *me = static_cast(ob->data); - const MPoly *mp; MDeformWeight *dw, *dw_prev; int vgroup_active, vgroup_mirror = -1; - uint index; const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; /* mutually exclusive, could be made into a */ const short paint_selmode = ME_EDIT_PAINT_SEL_MODE(me); - const MPoly *polys = BKE_mesh_polys(me); - const MLoop *loops = BKE_mesh_loops(me); + const blender::Span polys = me->polys(); + const blender::Span loops = me->loops(); MDeformVert *dvert = BKE_mesh_deform_verts_for_write(me); if (me->totpoly == 0 || dvert == nullptr) { @@ -470,15 +468,16 @@ static bool weight_paint_set(Object *ob, float paintweight) const bool *select_poly = (const bool *)CustomData_get_layer_named( &me->pdata, CD_PROP_BOOL, ".select_poly"); - for (index = 0, mp = polys; index < me->totpoly; index++, mp++) { - uint fidx = mp->totloop - 1; + for (const int i : polys.index_range()) { + const MPoly &poly = polys[i]; + uint fidx = poly.totloop - 1; - if ((paint_selmode == SCE_SELECT_FACE) && !(select_poly && select_poly[index])) { + if ((paint_selmode == SCE_SELECT_FACE) && !(select_poly && select_poly[i])) { continue; } do { - uint vidx = loops[mp->loopstart + fidx].v; + uint vidx = loops[poly.loopstart + fidx].v; if (!dvert[vidx].flag) { if ((paint_selmode == SCE_SELECT_VERTEX) && !(select_vert && select_vert[vidx])) { @@ -517,7 +516,7 @@ static bool weight_paint_set(Object *ob, float paintweight) { MDeformVert *dv = dvert; - for (index = me->totvert; index != 0; index--, dv++) { + for (int index = me->totvert; index != 0; index--, dv++) { dv->flag = 0; } } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index d1751625937..ebe483cbd25 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6201,15 +6201,15 @@ void SCULPT_boundary_info_ensure(Object *object) } Mesh *base_mesh = BKE_mesh_from_object(object); - const MEdge *edges = BKE_mesh_edges(base_mesh); - const MPoly *polys = BKE_mesh_polys(base_mesh); - const MLoop *loops = BKE_mesh_loops(base_mesh); + const blender::Span edges = base_mesh->edges(); + const blender::Span polys = base_mesh->polys(); + const blender::Span loops = base_mesh->loops(); ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info"); int *adjacent_faces_edge_count = static_cast( MEM_calloc_arrayN(base_mesh->totedge, sizeof(int), "Adjacent face edge count")); - for (int p = 0; p < base_mesh->totpoly; p++) { + for (const int p : polys.index_range()) { const MPoly *poly = &polys[p]; for (int l = 0; l < poly->totloop; l++) { const MLoop *loop = &loops[l + poly->loopstart]; @@ -6217,7 +6217,7 @@ void SCULPT_boundary_info_ensure(Object *object) } } - for (int e = 0; e < base_mesh->totedge; e++) { + for (const int e : edges.index_range()) { if (adjacent_faces_edge_count[e] < 2) { const MEdge *edge = &edges[e]; BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true); diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 7801741d5d8..d7f1e0def14 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -83,7 +83,7 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br) { - if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) { + if (ss && br && SCULPT_stroke_is_dynamic_topology(ss, br)) { return false; } if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) { @@ -195,7 +195,7 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush); - if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY && + if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY && brush && sculpt_automasking_is_constrained_by_radius(brush)) { return true; } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.cc b/source/blender/editors/sculpt_paint/sculpt_cloth.cc index 3ceab5431ec..6db4fe5b718 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.cc @@ -1571,8 +1571,13 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_stroke_id_next(ob); SCULPT_undo_push_begin(ob, op); - SCULPT_filter_cache_init( - C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius")); + SCULPT_filter_cache_init(C, + ob, + sd, + SCULPT_UNDO_COORDS, + event->mval, + RNA_float_get(op->ptr, "area_normal_radius"), + RNA_float_get(op->ptr, "strength")); ss->filter_cache->automasking = SCULPT_automasking_cache_init(sd, nullptr, ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.cc b/source/blender/editors/sculpt_paint/sculpt_expand.cc index e6a69501ac5..e38ad0fcd2d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.cc +++ b/source/blender/editors/sculpt_paint/sculpt_expand.cc @@ -791,10 +791,10 @@ static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss, Mesh *mesh, ExpandCache *expand_cache) { - const MPoly *polys = BKE_mesh_polys(mesh); + const blender::Span polys = mesh->polys(); const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - for (int p = 0; p < mesh->totpoly; p++) { + for (const int p : polys.index_range()) { const MPoly *poly = &polys[p]; float accum = 0.0f; for (int l = 0; l < poly->totloop; l++) { @@ -809,10 +809,10 @@ static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss, static void sculpt_expand_vertex_to_faces_falloff(Mesh *mesh, ExpandCache *expand_cache) { - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); - for (int p = 0; p < mesh->totpoly; p++) { + for (const int p : polys.index_range()) { const MPoly *poly = &polys[p]; float accum = 0.0f; for (int l = 0; l < poly->totloop; l++) { @@ -1987,8 +1987,8 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets, { const int totface = ss->totfaces; MeshElemMap *pmap = ss->pmap; - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); /* Check that all the face sets IDs in the mesh are not equal to `delete_id` * before attempting to delete it. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index de06fa3541a..46f247aeccf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -1102,10 +1102,10 @@ static void sculpt_face_set_grow(Object *ob, const bool modify_hidden) { Mesh *mesh = BKE_mesh_from_object(ob); - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); - for (int p = 0; p < mesh->totpoly; p++) { + for (const int p : polys.index_range()) { if (!modify_hidden && prev_face_sets[p] <= 0) { continue; } @@ -1133,9 +1133,9 @@ static void sculpt_face_set_shrink(Object *ob, const bool modify_hidden) { Mesh *mesh = BKE_mesh_from_object(ob); - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); - for (int p = 0; p < mesh->totpoly; p++) { + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); + for (const int p : polys.index_range()) { if (!modify_hidden && prev_face_sets[p] <= 0) { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc index 3ca6449ad50..36d02c65605 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc @@ -347,8 +347,13 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - SCULPT_filter_cache_init( - C, ob, sd, SCULPT_UNDO_COLOR, event->mval, RNA_float_get(op->ptr, "area_normal_radius")); + SCULPT_filter_cache_init(C, + ob, + sd, + SCULPT_UNDO_COLOR, + event->mval, + RNA_float_get(op->ptr, "area_normal_radius"), + RNA_float_get(op->ptr, "strength")); FilterCache *filter_cache = ss->filter_cache; filter_cache->active_face_set = SCULPT_FACE_SET_NONE; filter_cache->automasking = SCULPT_automasking_cache_init(sd, nullptr, ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index 82f89c7f8f9..7ce0f822330 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -8,7 +8,9 @@ #include "MEM_guardedalloc.h" #include "BLI_hash.h" +#include "BLI_index_range.hh" #include "BLI_math.h" +#include "BLI_math_vector_types.hh" #include "BLI_task.h" #include "DNA_meshdata_types.h" @@ -23,6 +25,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "ED_screen.h" #include "ED_view3d.h" #include "paint_intern.h" @@ -30,14 +33,20 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_prototypes.h" #include "UI_interface.h" +#include "UI_resources.h" #include "bmesh.h" #include #include +using blender::float2; +using blender::float3; +using blender::IndexRange; + void SCULPT_filter_to_orientation_space(float r_v[3], FilterCache *filter_cache) { switch (filter_cache->orientation) { @@ -96,13 +105,14 @@ void SCULPT_filter_cache_init(bContext *C, Sculpt *sd, const int undo_type, const int mval[2], - float area_normal_radius) + float area_normal_radius, + float start_strength) { SculptSession *ss = ob->sculpt; PBVH *pbvh = ob->sculpt->pbvh; ss->filter_cache = MEM_cnew(__func__); - + ss->filter_cache->start_filter_strength = start_strength; ss->filter_cache->random_seed = rand(); if (undo_type == SCULPT_UNDO_COLOR) { @@ -338,6 +348,15 @@ static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type) MESH_FILTER_SHARPEN); } +static bool sculpt_mesh_filter_is_continuous(eSculptMeshFilterType type) +{ + return (ELEM(type, + MESH_FILTER_SHARPEN, + MESH_FILTER_SMOOTH, + MESH_FILTER_RELAX, + MESH_FILTER_RELAX_FACE_SETS)); +} + static void mesh_filter_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict /*tls*/) @@ -379,7 +398,8 @@ static void mesh_filter_task_cb(void *__restrict userdata, continue; } - if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) { + if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS) || + ss->filter_cache->no_orig_co) { copy_v3_v3(orig_co, vd.co); } else { @@ -681,34 +701,16 @@ static void mesh_filter_surface_smooth_displace_task_cb(void *__restrict userdat BKE_pbvh_vertex_iter_end; } -static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event) +static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type")); float filter_strength = RNA_float_get(op->ptr, "strength"); - if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { - SCULPT_filter_cache_free(ss); - SCULPT_undo_push_end(ob); - SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); - return OPERATOR_FINISHED; - } - - if (event->type != MOUSEMOVE) { - return OPERATOR_RUNNING_MODAL; - } - - const float len = event->prev_press_xy[0] - event->xy[0]; - filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC; - SCULPT_vertex_random_access_ensure(ss); - bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type); - BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false); - SculptThreadedTaskData data{}; data.sd = sd; data.ob = ob; @@ -740,22 +742,170 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * } SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS); +} + +static void sculpt_mesh_update_strength(wmOperator *op, + SculptSession *ss, + float2 prev_press_mouse, + float2 mouse) +{ + const float len = prev_press_mouse[0] - mouse[0]; + + float filter_strength = ss->filter_cache->start_filter_strength * -len * 0.001f * UI_DPI_FAC; + RNA_float_set(op->ptr, "strength", filter_strength); +} +static void sculpt_mesh_filter_apply_with_history(bContext *C, wmOperator *op) +{ + /* Event history is only stored for smooth and relax filters. */ + if (!RNA_collection_length(op->ptr, "event_history")) { + sculpt_mesh_filter_apply(C, op); + return; + } + + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + float2 start_mouse; + bool first = true; + float initial_strength = ss->filter_cache->start_filter_strength; + + RNA_BEGIN (op->ptr, item, "event_history") { + float2 mouse; + RNA_float_get_array(&item, "mouse_event", mouse); + + if (first) { + first = false; + start_mouse = mouse; + continue; + } + + sculpt_mesh_update_strength(op, ss, start_mouse, mouse); + sculpt_mesh_filter_apply(C, op); + } + RNA_END; + + RNA_float_set(op->ptr, "strength", initial_strength); +} + +static void sculpt_mesh_filter_end(bContext *C, wmOperator * /*op*/) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + SCULPT_filter_cache_free(ss); + SCULPT_undo_push_end(ob); + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); +} + +static void sculpt_mesh_filter_cancel(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + PBVHNode **nodes; + int nodes_num; + + if (!ss || !ss->pbvh) { + return; + } + + /* Gather all PBVH leaf nodes. */ + BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &nodes_num); + + for (int i : IndexRange(nodes_num)) { + PBVHNode *node = nodes[i]; + PBVHVertexIter vd; + + SculptOrigVertData orig_data; + SCULPT_orig_vert_data_init(&orig_data, ob, nodes[i], SCULPT_UNDO_COORDS); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + + copy_v3_v3(vd.co, orig_data.co); + } + BKE_pbvh_vertex_iter_end; + + BKE_pbvh_node_mark_update(node); + } + + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB); +} + +static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + SculptSession *ss = ob->sculpt; + const eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type")); + + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { + float initial_strength = ss->filter_cache->start_filter_strength; + sculpt_mesh_filter_end(C, op); + + /* Don't update strength property if we're storing an event history. */ + if (sculpt_mesh_filter_is_continuous(filter_type)) { + RNA_float_set(op->ptr, "strength", initial_strength); + } + + return OPERATOR_FINISHED; + } + + if (event->type != MOUSEMOVE) { + return OPERATOR_RUNNING_MODAL; + } + + /* Note: some filter types are continuous, for these we store an + * event history in RNA for continuous. + * This way the user can tweak the last operator properties + * or repeat the op and get expected results. */ + if (sculpt_mesh_filter_is_continuous(filter_type)) { + if (RNA_collection_length(op->ptr, "event_history") == 0) { + /* First entry is the start mouse position, event->prev_press_xy. */ + PointerRNA startptr; + RNA_collection_add(op->ptr, "event_history", &startptr); + + float2 mouse_start(float(event->prev_press_xy[0]), float(event->prev_press_xy[1])); + RNA_float_set_array(&startptr, "mouse_event", mouse_start); + } + + PointerRNA itemptr; + RNA_collection_add(op->ptr, "event_history", &itemptr); + + float2 mouse(float(event->xy[0]), float(event->xy[1])); + RNA_float_set_array(&itemptr, "mouse_event", mouse); + RNA_float_set(&itemptr, "pressure", WM_event_tablet_data(event, nullptr, nullptr)); + } + + float2 prev_mval(float(event->prev_press_xy[0]), float(event->prev_press_xy[1])); + float2 mval(float(event->xy[0]), float(event->xy[1])); + + sculpt_mesh_update_strength(op, ss, prev_mval, mval); + + bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type); + BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false); + + sculpt_mesh_filter_apply(C, op); return OPERATOR_RUNNING_MODAL; } -static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event) +/* Returns OPERATOR_PASS_THROUGH on success. */ +static int sculpt_mesh_filter_start(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + int mval[2]; + RNA_int_get_array(op->ptr, "start_mouse", mval); + + const eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type")); + const bool use_automasking = SCULPT_is_automasking_enabled(sd, nullptr, nullptr); + const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type) || use_automasking; + + BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false); SculptSession *ss = ob->sculpt; const eMeshFilterDeformAxis deform_axis = eMeshFilterDeformAxis( RNA_enum_get(op->ptr, "deform_axis")); - const eSculptMeshFilterType filter_type = eSculptMeshFilterType(RNA_enum_get(op->ptr, "type")); - const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, nullptr); - const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type) || use_automasking; if (deform_axis == 0) { /* All axis are disabled, so the filter is not going to produce any deformation. */ @@ -768,21 +918,25 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent /* Update the active face set manually as the paint cursor is not enabled when using the Mesh * Filter Tool. */ - float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; + float mval_fl[2] = {float(mval[0]), float(mval[1])}; SculptCursorGeometryInfo sgi; SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); } SCULPT_vertex_random_access_ensure(ss); - BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false); if (needs_topology_info) { SCULPT_boundary_info_ensure(ob); } SCULPT_undo_push_begin(ob, op); - SCULPT_filter_cache_init( - C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius")); + SCULPT_filter_cache_init(C, + ob, + sd, + SCULPT_UNDO_COORDS, + mval, + RNA_float_get(op->ptr, "area_normal_radius"), + RNA_float_get(op->ptr, "strength")); FilterCache *filter_cache = ss->filter_cache; filter_cache->active_face_set = SCULPT_FACE_SET_NONE; @@ -826,12 +980,56 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent RNA_enum_get(op->ptr, "orientation")); ss->filter_cache->orientation = orientation; - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_PASS_THROUGH; +} + +static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + RNA_int_set_array(op->ptr, "start_mouse", event->mval); + int ret = sculpt_mesh_filter_start(C, op); + + if (ret == OPERATOR_PASS_THROUGH) { + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; + } + + return ret; +} + +static int sculpt_mesh_filter_exec(bContext *C, wmOperator *op) +{ + int ret = sculpt_mesh_filter_start(C, op); + + if (ret == OPERATOR_PASS_THROUGH) { + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + int iterations = RNA_int_get(op->ptr, "iteration_count"); + bool has_history = RNA_collection_length(op->ptr, "event_history") > 0; + + if (!has_history) { + ss->filter_cache->no_orig_co = true; + } + + for (int i = 0; i < iterations; i++) { + sculpt_mesh_filter_apply_with_history(C, op); + + ss->filter_cache->no_orig_co = true; + } + + sculpt_mesh_filter_end(C, op); + + return OPERATOR_FINISHED; + } + + return ret; } void SCULPT_mesh_filter_properties(wmOperatorType *ot) { + RNA_def_int_array( + ot->srna, "start_mouse", 2, nullptr, 0, 1 << 14, "Starting Mouse", "", 0, 1 << 14); + RNA_def_float( ot->srna, "area_normal_radius", @@ -844,6 +1042,31 @@ void SCULPT_mesh_filter_properties(wmOperatorType *ot) 1.0); RNA_def_float( ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); + RNA_def_int(ot->srna, + "iteration_count", + 1, + 1, + 10000, + "Repeat", + "How many times to repeat the filter", + 1, + 100); + + /* Smooth filter requires entire event history. */ + PropertyRNA *prop = RNA_def_collection_runtime( + ot->srna, "event_history", &RNA_OperatorStrokeElement, "", ""); + RNA_def_property_flag(prop, PropertyFlag(int(PROP_HIDDEN) | int(PROP_SKIP_SAVE))); +} + +static void sculpt_mesh_ui_exec(bContext * /*C*/, wmOperator *op) +{ + uiLayout *layout = op->layout; + + uiItemR(layout, op->ptr, "strength", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "iteration_count", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "orientation", 0, nullptr, ICON_NONE); + layout = uiLayoutRow(layout, true); + uiItemR(layout, op->ptr, "deform_axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } void SCULPT_OT_mesh_filter(wmOperatorType *ot) @@ -857,6 +1080,8 @@ void SCULPT_OT_mesh_filter(wmOperatorType *ot) ot->invoke = sculpt_mesh_filter_invoke; ot->modal = sculpt_mesh_filter_modal; ot->poll = SCULPT_mode_poll; + ot->exec = sculpt_mesh_filter_exec; + ot->ui = sculpt_mesh_ui_exec; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc index 84232a295dd..2b85e09581e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc @@ -87,19 +87,25 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, const float limit_radius_sq = limit_radius * limit_radius; float(*vert_positions)[3] = SCULPT_mesh_deformed_positions_get(ss); - const MEdge *edges = BKE_mesh_edges(mesh); - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); + const blender::Span edges = mesh->edges(); + const blender::Span polys = mesh->polys(); + const blender::Span loops = mesh->loops(); float *dists = static_cast(MEM_malloc_arrayN(totvert, sizeof(float), __func__)); BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag"); if (!ss->epmap) { - BKE_mesh_edge_poly_map_create( - &ss->epmap, &ss->epmap_mem, mesh->totedge, polys, mesh->totpoly, loops, mesh->totloop); + BKE_mesh_edge_poly_map_create(&ss->epmap, + &ss->epmap_mem, + edges.size(), + polys.data(), + polys.size(), + loops.data(), + loops.size()); } if (!ss->vemap) { - BKE_mesh_vert_edge_map_create(&ss->vemap, &ss->vemap_mem, edges, mesh->totvert, mesh->totedge); + BKE_mesh_vert_edge_map_create( + &ss->vemap, &ss->vemap_mem, edges.data(), mesh->totvert, edges.size()); } /* Both contain edge indices encoded as *void. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 801cf35889b..bee2e37df9c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -496,6 +496,8 @@ struct FilterCache { float (*pre_smoothed_color)[4]; ViewContext vc; + float start_filter_strength; + bool no_orig_co; }; /** @@ -1440,7 +1442,8 @@ void SCULPT_filter_cache_init(bContext *C, Sculpt *sd, int undo_type, const int mval[2], - float area_normal_radius); + float area_normal_radius, + float start_strength); void SCULPT_filter_cache_free(SculptSession *ss); void SCULPT_mesh_filter_properties(wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.cc b/source/blender/editors/sculpt_paint/sculpt_transform.cc index 9b487675c88..17e23a726c1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.cc +++ b/source/blender/editors/sculpt_paint/sculpt_transform.cc @@ -58,7 +58,7 @@ void ED_sculpt_init_transform(bContext *C, Object *ob, const int mval[2], const SCULPT_vertex_random_access_ensure(ss); - SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS, mval, 5.0); + SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS, mval, 5.0, 1.0f); if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) { ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL; diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index bb8456f6486..1bd28b068f9 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -590,7 +590,7 @@ void ED_animedit_unlink_action( if (nlt->strips.first == NULL) { BLI_assert(nstrip == NULL); - BKE_nlatrack_free(&adt->nla_tracks, nlt, true); + BKE_nlatrack_remove_and_free(&adt->nla_tracks, nlt, true); } } } diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index 5e740f897fc..47f82ffe999 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -61,7 +61,8 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region) items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); const int height = ANIM_UI_get_channels_total_height(v2d, items); - v2d->tot.ymin = -height; + const float pad_bottom = BLI_listbase_is_empty(ac->markers) ? 0 : UI_MARKER_MARGIN_Y; + v2d->tot.ymin = -(height + pad_bottom); /* need to do a view-sync here, so that the keys area doesn't jump around (it must copy this) */ UI_view2d_sync(NULL, ac->area, v2d, V2D_LOCK_COPY); @@ -195,7 +196,8 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region size_t items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); const int height = ANIM_UI_get_channels_total_height(v2d, items); - v2d->tot.ymin = -height; + const float pad_bottom = BLI_listbase_is_empty(ac->markers) ? 0 : UI_MARKER_MARGIN_Y; + v2d->tot.ymin = -(height + pad_bottom); /* Draw the manual frame ranges for actions in the background of the dopesheet. * The action editor has already drawn the range for its action so it's not needed. */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 094eccf4114..d9772844358 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -533,8 +533,9 @@ static eKeyPasteError paste_action_keys(bAnimContext *ac, ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); } - /* paste keyframes */ - const eKeyPasteError ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip); + /* Value offset is always None because the user cannot see the effect of it. */ + const eKeyPasteError ok = paste_animedit_keys( + ac, &anim_data, offset_mode, KEYFRAME_PASTE_VALUE_OFFSET_NONE, merge_mode, flip); /* clean up */ ANIM_animdata_freelist(&anim_data); diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 5a7be3be4a5..34c89a13288 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -400,6 +400,21 @@ static void saction_channel_region_message_subscribe(const wmRegionMessageSubscr } } +static void action_clamp_scroll(ARegion *region) +{ + View2D *v2d = ®ion->v2d; + const float cur_height_y = BLI_rctf_size_y(&v2d->cur); + + if (BLI_rctf_size_y(&v2d->cur) > BLI_rctf_size_y(&v2d->tot)) { + v2d->cur.ymin = -cur_height_y; + v2d->cur.ymax = 0; + } + else if (v2d->cur.ymin < v2d->tot.ymin) { + v2d->cur.ymin = v2d->tot.ymin; + v2d->cur.ymax = v2d->cur.ymin + cur_height_y; + } +} + static void action_main_region_listener(const wmRegionListenerParams *params) { ARegion *region = params->region; @@ -860,6 +875,13 @@ static void action_blend_write(BlendWriter *writer, SpaceLink *sl) BLO_write_struct(writer, SpaceAction, sl); } +static void action_main_region_view2d_changed(const bContext *UNUSED(C), ARegion *region) +{ + /* V2D_KEEPTOT_STRICT cannot be used to clamp scrolling + * because it also clamps the x-axis to 0. */ + action_clamp_scroll(region); +} + void ED_spacetype_action(void) { SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype action"); @@ -892,6 +914,7 @@ void ED_spacetype_action(void) art->draw_overlay = action_main_region_draw_overlay; art->listener = action_main_region_listener; art->message_subscribe = saction_main_region_message_subscribe; + art->on_view2d_changed = action_main_region_view2d_changed; art->keymapflag = ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES; BLI_addhead(&st->regiontypes, art); diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 5bae8238bca..bfc1903ae7d 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -128,16 +128,55 @@ static void draw_tile_background(const rcti *draw_rect, int colorid, int shade) UI_draw_roundbox_aa(&draw_rect_fl, true, 5.0f, color); } -static void file_draw_icon(const SpaceFile *sfile, - uiBlock *block, - const FileDirEntry *file, - const char *path, - const rcti *tile_draw_rect, - int icon, - int width, - int height, - bool drag, - bool dimmed) +static void file_but_enable_drag(uiBut *but, + const SpaceFile *sfile, + const FileDirEntry *file, + const char *path, + ImBuf *preview_image, + int icon, + float scale) +{ + ID *id; + + if ((id = filelist_file_get_id(file))) { + UI_but_drag_set_id(but, id); + if (preview_image) { + UI_but_drag_attach_image(but, preview_image, scale); + } + } + else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS && + (file->typeflag & FILE_TYPE_ASSET) != 0) { + char blend_path[FILE_MAX_LIBEXTRA]; + if (BLO_library_path_explode(path, blend_path, NULL, NULL)) { + const int import_method = ED_fileselect_asset_import_method_get(sfile, file); + BLI_assert(import_method > -1); + + UI_but_drag_set_asset(but, + &(AssetHandle){.file_data = file}, + BLI_strdup(blend_path), + import_method, + icon, + preview_image, + scale); + } + } + else if (preview_image) { + UI_but_drag_set_image(but, BLI_strdup(path), icon, preview_image, scale, true); + } + else { + /* path is no more static, cannot give it directly to but... */ + UI_but_drag_set_path(but, BLI_strdup(path), true); + } +} + +static uiBut *file_add_icon_but(const SpaceFile *sfile, + uiBlock *block, + const char *path, + const rcti *tile_draw_rect, + int icon, + int width, + int height, + bool dimmed) { uiBut *but; @@ -151,39 +190,7 @@ static void file_draw_icon(const SpaceFile *sfile, block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, a1, a2, NULL); UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path), MEM_freeN); - if (drag) { - /* TODO: duplicated from file_draw_preview(). */ - ID *id; - - if ((id = filelist_file_get_id(file))) { - UI_but_drag_set_id(but, id); - ImBuf *preview_image = filelist_file_getimage(file); - if (preview_image) { - UI_but_drag_attach_image(but, preview_image, UI_DPI_FAC); - } - } - else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS && - (file->typeflag & FILE_TYPE_ASSET) != 0) { - ImBuf *preview_image = filelist_file_getimage(file); - char blend_path[FILE_MAX_LIBEXTRA]; - if (BLO_library_path_explode(path, blend_path, NULL, NULL)) { - const int import_method = ED_fileselect_asset_import_method_get(sfile, file); - BLI_assert(import_method > -1); - - UI_but_drag_set_asset(but, - &(AssetHandle){.file_data = file}, - BLI_strdup(blend_path), - import_method, - icon, - preview_image, - UI_DPI_FAC); - } - } - else { - /* path is no more static, cannot give it directly to but... */ - UI_but_drag_set_path(but, BLI_strdup(path), true); - } - } + return but; } static void file_draw_string(int sx, @@ -294,21 +301,50 @@ void file_calc_previews(const bContext *C, ARegion *region) UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height); } -static void file_draw_preview(const SpaceFile *sfile, - uiBlock *block, - const FileDirEntry *file, - const char *path, +static void file_add_preview_drag_but(const SpaceFile *sfile, + uiBlock *block, + FileLayout *layout, + const FileDirEntry *file, + const char *path, + const rcti *tile_draw_rect, + ImBuf *preview_image, + const int icon, + const float scale) +{ + /* Invisible button for dragging. */ + rcti drag_rect = *tile_draw_rect; + /* A bit smaller than the full tile, to increase the gap between items that users can drag from + * for box select. */ + BLI_rcti_pad(&drag_rect, -layout->tile_border_x, -layout->tile_border_y); + + uiBut *but = uiDefBut(block, + UI_BTYPE_LABEL, + 0, + "", + drag_rect.xmin, + drag_rect.ymin, + BLI_rcti_size_x(&drag_rect), + BLI_rcti_size_y(&drag_rect), + NULL, + 0.0, + 0.0, + 0, + 0, + NULL); + file_but_enable_drag(but, sfile, file, path, preview_image, icon, scale); +} + +static void file_draw_preview(const FileDirEntry *file, const rcti *tile_draw_rect, const float icon_aspect, ImBuf *imb, const int icon, FileLayout *layout, const bool is_icon, - const bool drag, const bool dimmed, - const bool is_link) + const bool is_link, + float *r_scale) { - uiBut *but; float fx, fy; float dx, dy; int xco, yco; @@ -525,59 +561,11 @@ static void file_draw_preview(const SpaceFile *sfile, immUnbindProgram(); } - /* Invisible button for dragging. */ - rcti drag_rect = *tile_draw_rect; - /* A bit smaller than the full tile, to increase the gap between items that users can drag from - * for box select. */ - BLI_rcti_pad(&drag_rect, -layout->tile_border_x, -layout->tile_border_y); - - but = uiDefBut(block, - UI_BTYPE_LABEL, - 0, - "", - drag_rect.xmin, - drag_rect.ymin, - BLI_rcti_size_x(&drag_rect), - BLI_rcti_size_y(&drag_rect), - NULL, - 0.0, - 0.0, - 0, - 0, - NULL); - - /* Drag-region. */ - if (drag) { - ID *id; - - if ((id = filelist_file_get_id(file))) { - UI_but_drag_set_id(but, id); - UI_but_drag_attach_image(but, imb, scale); - } - /* path is no more static, cannot give it directly to but... */ - else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS && - (file->typeflag & FILE_TYPE_ASSET) != 0) { - char blend_path[FILE_MAX_LIBEXTRA]; - - if (BLO_library_path_explode(path, blend_path, NULL, NULL)) { - const int import_method = ED_fileselect_asset_import_method_get(sfile, file); - BLI_assert(import_method > -1); - - UI_but_drag_set_asset(but, - &(AssetHandle){.file_data = file}, - BLI_strdup(blend_path), - import_method, - icon, - imb, - scale); - } - } - else { - UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true); - } - } - GPU_blend(GPU_BLEND_NONE); + + if (r_scale) { + *r_scale = scale; + } } static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname) @@ -1030,31 +1018,36 @@ void file_draw_list(const bContext *C, ARegion *region) is_icon = 1; } - file_draw_preview(sfile, - block, - file, - path, + float scale = 0; + file_draw_preview(file, &tile_draw_rect, thumb_icon_aspect, imb, icon, layout, is_icon, - do_drag, is_hidden, - is_link); + is_link, + /* Returns the scale which is needed below. */ + &scale); + if (do_drag) { + file_add_preview_drag_but( + sfile, block, layout, file, path, &tile_draw_rect, imb, icon, scale); + } } else { - file_draw_icon(sfile, - block, - file, - path, - &tile_draw_rect, - filelist_geticon(files, i, true), - ICON_DEFAULT_WIDTH_SCALE, - ICON_DEFAULT_HEIGHT_SCALE, - do_drag, - is_hidden); + const int icon = filelist_geticon(files, i, true); + uiBut *icon_but = file_add_icon_but(sfile, + block, + path, + &tile_draw_rect, + icon, + ICON_DEFAULT_WIDTH_SCALE, + ICON_DEFAULT_HEIGHT_SCALE, + is_hidden); + if (do_drag) { + file_but_enable_drag(icon_but, sfile, file, path, NULL, icon, UI_DPI_FAC); + } icon_ofs += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X; } diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index b1f4673f7eb..73e05bbe1e3 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -475,6 +475,7 @@ static short copy_graph_keys(bAnimContext *ac) static eKeyPasteError paste_graph_keys(bAnimContext *ac, const eKeyPasteOffset offset_mode, + const eKeyPasteValueOffset value_offset_mode, const eKeyMergeMode merge_mode, bool flip) { @@ -495,7 +496,8 @@ static eKeyPasteError paste_graph_keys(bAnimContext *ac, } /* Paste keyframes. */ - const eKeyPasteError ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip); + const eKeyPasteError ok = paste_animedit_keys( + ac, &anim_data, offset_mode, value_offset_mode, merge_mode, flip); /* Clean up. */ ANIM_animdata_freelist(&anim_data); @@ -544,6 +546,7 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op) bAnimContext ac; const eKeyPasteOffset offset_mode = RNA_enum_get(op->ptr, "offset"); + const eKeyPasteValueOffset value_offset_mode = RNA_enum_get(op->ptr, "value_offset"); const eKeyMergeMode merge_mode = RNA_enum_get(op->ptr, "merge"); const bool flipped = RNA_boolean_get(op->ptr, "flipped"); @@ -555,7 +558,8 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op) /* Ac.reports by default will be the global reports list, which won't show warnings. */ ac.reports = op->reports; - const eKeyPasteError kf_empty = paste_graph_keys(&ac, offset_mode, merge_mode, flipped); + const eKeyPasteError kf_empty = paste_graph_keys( + &ac, offset_mode, value_offset_mode, merge_mode, flipped); switch (kf_empty) { case KEYFRAME_PASTE_OK: break; @@ -614,8 +618,14 @@ void GRAPH_OT_paste(wmOperatorType *ot) "offset", rna_enum_keyframe_paste_offset_items, KEYFRAME_PASTE_OFFSET_CFRA_START, - "Offset", + "Frame Offset", "Paste time offset of keys"); + RNA_def_enum(ot->srna, + "value_offset", + rna_enum_keyframe_paste_offset_value, + KEYFRAME_PASTE_VALUE_OFFSET_NONE, + "Value Offset", + "Paste keys with a value offset"); RNA_def_enum(ot->srna, "merge", rna_enum_keyframe_paste_merge_items, diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c index f80c7c17c3a..e6cbdd63ae6 100644 --- a/source/blender/editors/space_graph/graph_view.c +++ b/source/blender/editors/space_graph/graph_view.c @@ -91,7 +91,7 @@ void get_graph_keyframe_extents(bAnimContext *ac, /* Get range. */ if (BKE_fcurve_calc_bounds( - fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles)) { + fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles, NULL)) { short mapping_flag = ANIM_get_normalization_flags(ac); /* Apply NLA scaling. */ diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index a783ee32779..1d7645d4e67 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -736,7 +736,7 @@ static int nlaedit_delete_tracks_exec(bContext *C, wmOperator *UNUSED(op)) } /* call delete on this track - deletes all strips too */ - BKE_nlatrack_free(&adt->nla_tracks, nlt, true); + BKE_nlatrack_remove_and_free(&adt->nla_tracks, nlt, true); ale->update = ANIM_UPDATE_DEPS; } } diff --git a/source/blender/editors/space_node/add_node_search.cc b/source/blender/editors/space_node/add_node_search.cc index 16505620e6a..281995f59bc 100644 --- a/source/blender/editors/space_node/add_node_search.cc +++ b/source/blender/editors/space_node/add_node_search.cc @@ -26,6 +26,8 @@ #include "WM_api.h" +#include "NOD_add_node_search.hh" + #include "ED_asset.h" #include "ED_node.h" @@ -36,12 +38,9 @@ struct bContext; namespace blender::ed::space_node { struct AddNodeItem { - std::string ui_name; + nodes::AddNodeInfo info; std::string identifier; - std::string description; std::optional asset; - std::function after_add_fn; - int weight = 0; }; struct AddNodeSearchStorage { @@ -77,11 +76,11 @@ static void search_items_for_asset_metadata(const bNodeTree &node_tree, } AddNodeItem item{}; - item.ui_name = ED_asset_handle_get_name(&asset); + item.info.ui_name = ED_asset_handle_get_name(&asset); item.identifier = node_tree.typeinfo->group_idname; - item.description = asset_data.description == nullptr ? "" : asset_data.description; + item.info.description = asset_data.description == nullptr ? "" : asset_data.description; item.asset = asset; - item.after_add_fn = [asset](const bContext &C, bNodeTree &node_tree, bNode &node) { + item.info.after_add_fn = [asset](const bContext &C, bNodeTree &node_tree, bNode &node) { Main &bmain = *CTX_data_main(&C); node.flag &= ~NODE_OPTIONS; node.id = asset::get_local_id_from_asset_or_append_and_reuse(bmain, asset); @@ -139,9 +138,9 @@ static void gather_search_items_for_node_groups(const bContext &C, continue; } AddNodeItem item{}; - item.ui_name = node_group->id.name + 2; + item.info.ui_name = node_group->id.name + 2; item.identifier = node_tree.typeinfo->group_idname; - item.after_add_fn = [node_group](const bContext &C, bNodeTree &node_tree, bNode &node) { + item.info.after_add_fn = [node_group](const bContext &C, bNodeTree &node_tree, bNode &node) { Main &bmain = *CTX_data_main(&C); node.id = &node_group->id; id_us_plus(node.id); @@ -161,19 +160,18 @@ static void gather_add_node_operations(const bContext &C, if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) { continue; } - if (StringRefNull(node_tree.typeinfo->group_idname) == node_type->idname) { - /* Skip the empty group type. */ + if (!node_type->gather_add_node_search_ops) { continue; } - if (StringRefNull(node_type->ui_name).endswith("(Legacy)")) { - continue; + Vector info_items; + nodes::GatherAddNodeSearchParams params(*node_type, node_tree, info_items); + node_type->gather_add_node_search_ops(params); + for (nodes::AddNodeInfo &info : info_items) { + AddNodeItem item{}; + item.info = std::move(info); + item.identifier = node_type->idname; + r_search_items.append(item); } - - AddNodeItem item{}; - item.ui_name = IFACE_(node_type->ui_name); - item.identifier = node_type->idname; - item.description = TIP_(node_type->ui_description); - r_search_items.append(std::move(item)); } NODE_TYPES_END; @@ -199,7 +197,7 @@ static void add_node_search_update_fn( StringSearch *search = BLI_string_search_new(); for (AddNodeItem &item : storage.search_add_items) { - BLI_string_search_add(search, item.ui_name.c_str(), &item, item.weight); + BLI_string_search_add(search, item.info.ui_name.c_str(), &item, item.info.weight); } /* Don't filter when the menu is first opened, but still run the search @@ -210,7 +208,7 @@ static void add_node_search_update_fn( for (const int i : IndexRange(filtered_amount)) { AddNodeItem &item = *filtered_items[i]; - if (!UI_search_item_add(items, item.ui_name.c_str(), &item, ICON_NONE, 0, 0)) { + if (!UI_search_item_add(items, item.info.ui_name.c_str(), &item, ICON_NONE, 0, 0)) { break; } } @@ -234,8 +232,8 @@ static void add_node_search_exec_fn(bContext *C, void *arg1, void *arg2) bNode *new_node = nodeAddNode(C, &node_tree, item->identifier.c_str()); BLI_assert(new_node != nullptr); - if (item->after_add_fn) { - item->after_add_fn(*C, node_tree, *new_node); + if (item->info.after_add_fn) { + item->info.after_add_fn(*C, node_tree, *new_node); } new_node->locx = storage.cursor.x / UI_DPI_FAC; @@ -266,7 +264,7 @@ static ARegion *add_node_search_tooltip_fn( uiSearchItemTooltipData tooltip_data{}; BLI_strncpy(tooltip_data.description, - item->asset ? item->description.c_str() : TIP_(item->description.c_str()), + item->asset ? item->info.description.c_str() : TIP_(item->info.description.c_str()), sizeof(tooltip_data.description)); return UI_tooltip_create_from_search_item_generic(C, region, item_rect, &tooltip_data); diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc index ce37057bcf3..b11997ba1b6 100644 --- a/source/blender/editors/space_node/node_view.cc +++ b/source/blender/editors/space_node/node_view.cc @@ -60,7 +60,7 @@ bool space_node_view_flag( int tot = 0; bool has_frame = false; if (snode.edittree) { - LISTBASE_FOREACH (const bNode *, node, &snode.edittree->nodes) { + for (const bNode *node : snode.edittree->all_nodes()) { if ((node->flag & node_flag) == node_flag) { BLI_rctf_union(&cur_new, &node->runtime->totr); tot++; diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index 842ba8d7fe8..1a276ce2090 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -35,10 +35,13 @@ 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 diff --git a/source/blender/editors/space_sequencer/sequencer_gizmo_retime.cc b/source/blender/editors/space_sequencer/sequencer_gizmo_retime.cc new file mode 100644 index 00000000000..9dcedbfdc4d --- /dev/null +++ b/source/blender/editors/space_sequencer/sequencer_gizmo_retime.cc @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \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; +} + +/** \} */ diff --git a/source/blender/editors/space_sequencer/sequencer_gizmo_retime_type.cc b/source/blender/editors/space_sequencer/sequencer_gizmo_retime_type.cc new file mode 100644 index 00000000000..ac224c08d7f --- /dev/null +++ b/source/blender/editors/space_sequencer/sequencer_gizmo_retime_type.cc @@ -0,0 +1,568 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \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; + +#define REMOVE_GIZMO_HEIGHT 14.0f * U.dpi_fac /* Pixels from bottom of strip. */ +#define RETIME_HANDLE_TRIANGLE_SIZE 14.0f * U.dpi_fac /* Size in pixels. */ +#define RETIME_HANDLE_MOUSEOVER_THRESHOLD 16.0f * U.dpi_fac /* Size in pixels. */ +#define RETIME_BUTTON_SIZE 0.6f /* Factor based on icon size. */ + +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 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_time_start_frame_get(seq) + handle->strip_frame_index + (is_last_handle ? 1 : 0); +} + +static const SeqRetimingHandle *mouse_over_handle_get(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(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) +{ + rctf rect = strip_box_get(C, seq); + rect.ymax = rect.ymin + REMOVE_GIZMO_HEIGHT; + 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) * U.dpi_fac; + const float icon_width = UI_icon_get_width(gizmo->icon_id) * U.dpi_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 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; +} 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(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 = RETIME_HANDLE_TRIANGLE_SIZE; + 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); + + if (seq == gizmo->mouse_over_seq && handle_x == gizmo->mouse_over_handle_x) { + immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } + else { + immUniformColor4f(0.65f, 0.65f, 0.65f, 1.0f); + } + + 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(seq, next_handle) < start_frame || handle_x_get(seq, handle) > end_frame) { + return; /* Label out of strip bounds. */ + } + + const float speed = SEQ_retiming_handle_speed_get(scene, seq, next_handle); + + char label_str[20]; + const size_t label_len = BLI_snprintf_rlen( + label_str, sizeof(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(seq, handle)); + const float xmax = min_ff(SEQ_time_right_handle_frame_get(scene, seq), + handle_x_get(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) +{ + const RetimeHandleMoveGizmo *gizmo = (RetimeHandleMoveGizmo *)gz; + const View2D *v2d = UI_view2d_fromcontext(C); + + 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(CTX_data_scene(C), 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(CTX_data_scene(C), seq); + const SeqRetimingHandle *handle = mouse_over_handle_get(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(seq, handle) == SEQ_time_left_handle_frame_get(scene, seq) || + handle_index == 0) { + return -1; + } + + rctf strip_box = strip_box_get(C, seq); + BLI_rctf_resize_x(&strip_box, BLI_rctf_size_x(&strip_box) + 2 * RETIME_HANDLE_TRIANGLE_SIZE); + if (!mouse_is_inside_box(&strip_box, mval)) { + return -1; + } + + gizmo->mouse_over_seq = seq; + gizmo->mouse_over_handle_x = handle_x_get(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(CTX_data_scene(C), seq); + const SeqRetimingHandle *handle = mouse_over_handle_get(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(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. */ + } + + rctf box = remove_box_get(C, seq); + + BLI_rctf_resize_x(&box, BLI_rctf_size_x(&box) + 2 * RETIME_HANDLE_TRIANGLE_SIZE); + 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", ""); +} + +/** \} */ diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index c6bc2bf7dec..d88ff6a5dff 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -10,11 +10,17 @@ #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; @@ -298,3 +304,21 @@ 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 diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index e0e2cfa42cc..bd2d53c1fca 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -68,6 +68,12 @@ 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); diff --git a/source/blender/editors/space_sequencer/sequencer_retiming.cc b/source/blender/editors/space_sequencer/sequencer_retiming.cc new file mode 100644 index 00000000000..84708b74e77 --- /dev/null +++ b/source/blender/editors/space_sequencer/sequencer_retiming.cc @@ -0,0 +1,377 @@ +/* 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) +{ + if (!sequencer_edit_poll(C)) { + return false; + } + + const Editing *ed = SEQ_editing_get(CTX_data_scene(C)); + Sequence *seq = ed->act_seq; + + if (seq != nullptr && !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); + 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(handle->strip_frame_index - 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(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; + } + + op->customdata = handle; + 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 = ®ion->v2d; + const Editing *ed = SEQ_editing_get(scene); + Sequence *seq = ed->act_seq; + + switch (event->type) { + case MOUSEMOVE: { + float mouse_x = UI_view2d_region_to_view_x(v2d, event->mval[0]); + int offset = 0; + + SeqRetimingHandle *handle = (SeqRetimingHandle *)op->customdata; + SeqRetimingHandle *handle_prev = handle - 1; + + /* Limit retiming handle movement. */ + int xmin = SEQ_time_start_frame_get(seq) + handle_prev->strip_frame_index + 1; + mouse_x = max_ff(xmin, mouse_x); + offset = mouse_x - (SEQ_time_start_frame_get(seq) + handle->strip_frame_index); + + 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; + + RNA_def_int(ot->srna, + "handle_index", + 0, + 0, + INT_MAX, + "Handle Index", + "Index of handle to be moved", + 0, + INT_MAX); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \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; + + 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); + } + + 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(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(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 */ + RNA_def_int(ot->srna, + "handle_index", + 0, + 0, + INT_MAX, + "Handle Index", + "Index of handle to be removed", + 0, + INT_MAX); +} + +/** \} */ diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 2f8ee37be2a..5661208dc15 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -494,10 +494,15 @@ 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); } @@ -645,6 +650,7 @@ 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; @@ -658,6 +664,7 @@ 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: @@ -668,6 +675,7 @@ 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; } @@ -1069,7 +1077,6 @@ 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); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 9e255f12eb5..2b7d981319c 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -68,6 +68,122 @@ std::unique_ptr ExtraColumns::get_column_values( return std::make_unique(column_id.name, GVArray::ForSpan(*values)); } +static void add_mesh_debug_column_names( + const Mesh &mesh, + const eAttrDomain domain, + FunctionRef fn) +{ + switch (domain) { + case ATTR_DOMAIN_POINT: + if (CustomData_has_layer(&mesh.vdata, CD_ORIGINDEX)) { + fn({(char *)"Original Index"}, false); + } + break; + case ATTR_DOMAIN_EDGE: + if (CustomData_has_layer(&mesh.edata, CD_ORIGINDEX)) { + fn({(char *)"Original Index"}, false); + } + fn({(char *)"Vertex 1"}, false); + fn({(char *)"Vertex 2"}, false); + break; + case ATTR_DOMAIN_FACE: + if (CustomData_has_layer(&mesh.pdata, CD_ORIGINDEX)) { + fn({(char *)"Original Index"}, false); + } + fn({(char *)"Corner Start"}, false); + fn({(char *)"Corner Size"}, false); + break; + case ATTR_DOMAIN_CORNER: + fn({(char *)"Vertex"}, false); + fn({(char *)"Edge"}, false); + break; + default: + BLI_assert_unreachable(); + break; + } +} + +static std::unique_ptr build_mesh_debug_columns(const Mesh &mesh, + const eAttrDomain domain, + const StringRef name) +{ + switch (domain) { + case ATTR_DOMAIN_POINT: { + if (name == "Original Index") { + const int *data = static_cast( + CustomData_get_layer(&mesh.vdata, CD_ORIGINDEX)); + if (data) { + return std::make_unique(name, VArray::ForSpan({data, mesh.totvert})); + } + } + return {}; + } + case ATTR_DOMAIN_EDGE: { + const Span edges = mesh.edges(); + if (name == "Original Index") { + const int *data = static_cast( + CustomData_get_layer(&mesh.edata, CD_ORIGINDEX)); + if (data) { + return std::make_unique(name, VArray::ForSpan({data, mesh.totedge})); + } + } + if (name == "Vertex 1") { + return std::make_unique( + name, VArray::ForFunc(edges.size(), [edges](int64_t index) { + return edges[index].v1; + })); + } + if (name == "Vertex 2") { + return std::make_unique( + name, VArray::ForFunc(edges.size(), [edges](int64_t index) { + return edges[index].v2; + })); + } + return {}; + } + case ATTR_DOMAIN_FACE: { + const Span polys = mesh.polys(); + if (name == "Original Index") { + const int *data = static_cast( + CustomData_get_layer(&mesh.pdata, CD_ORIGINDEX)); + if (data) { + return std::make_unique(name, VArray::ForSpan({data, mesh.totpoly})); + } + } + if (name == "Corner Start") { + return std::make_unique( + name, VArray::ForFunc(polys.size(), [polys](int64_t index) { + return polys[index].loopstart; + })); + } + if (name == "Corner Size") { + return std::make_unique( + name, VArray::ForFunc(polys.size(), [polys](int64_t index) { + return polys[index].totloop; + })); + } + return {}; + } + case ATTR_DOMAIN_CORNER: { + const Span loops = mesh.loops(); + if (name == "Vertex") { + return std::make_unique( + name, + VArray::ForFunc(loops.size(), [loops](int64_t index) { return loops[index].v; })); + } + if (name == "Edge") { + return std::make_unique( + name, + VArray::ForFunc(loops.size(), [loops](int64_t index) { return loops[index].e; })); + } + return {}; + } + default: + BLI_assert_unreachable(); + return {}; + } +} + void GeometryDataSource::foreach_default_column_ids( FunctionRef fn) const { @@ -109,17 +225,9 @@ void GeometryDataSource::foreach_default_column_ids( fn({(char *)"Scale"}, false); } else if (G.debug_value == 4001 && component_->type() == GEO_COMPONENT_TYPE_MESH) { - if (domain_ == ATTR_DOMAIN_EDGE) { - fn({(char *)"Vertex 1"}, false); - fn({(char *)"Vertex 2"}, false); - } - else if (domain_ == ATTR_DOMAIN_FACE) { - fn({(char *)"Corner Start"}, false); - fn({(char *)"Corner Size"}, false); - } - else if (domain_ == ATTR_DOMAIN_CORNER) { - fn({(char *)"Vertex"}, false); - fn({(char *)"Edge"}, false); + const MeshComponent &component = static_cast(*component_); + if (const Mesh *mesh = component.get_for_read()) { + add_mesh_debug_column_names(*mesh, domain_, fn); } } } @@ -174,51 +282,9 @@ std::unique_ptr GeometryDataSource::get_column_values( else if (G.debug_value == 4001 && component_->type() == GEO_COMPONENT_TYPE_MESH) { const MeshComponent &component = static_cast(*component_); if (const Mesh *mesh = component.get_for_read()) { - const Span edges = mesh->edges(); - const Span polys = mesh->polys(); - const Span loops = mesh->loops(); - - if (domain_ == ATTR_DOMAIN_EDGE) { - if (STREQ(column_id.name, "Vertex 1")) { - return std::make_unique( - column_id.name, VArray::ForFunc(edges.size(), [edges](int64_t index) { - return edges[index].v1; - })); - } - if (STREQ(column_id.name, "Vertex 2")) { - return std::make_unique( - column_id.name, VArray::ForFunc(edges.size(), [edges](int64_t index) { - return edges[index].v2; - })); - } - } - else if (domain_ == ATTR_DOMAIN_FACE) { - if (STREQ(column_id.name, "Corner Start")) { - return std::make_unique( - column_id.name, VArray::ForFunc(polys.size(), [polys](int64_t index) { - return polys[index].loopstart; - })); - } - if (STREQ(column_id.name, "Corner Size")) { - return std::make_unique( - column_id.name, VArray::ForFunc(polys.size(), [polys](int64_t index) { - return polys[index].totloop; - })); - } - } - else if (domain_ == ATTR_DOMAIN_CORNER) { - if (STREQ(column_id.name, "Vertex")) { - return std::make_unique( - column_id.name, VArray::ForFunc(loops.size(), [loops](int64_t index) { - return loops[index].v; - })); - } - if (STREQ(column_id.name, "Edge")) { - return std::make_unique( - column_id.name, VArray