WIP: Vulkan: Workbench #107886
|
@ -503,7 +503,7 @@ if(NOT APPLE)
|
|||
mark_as_advanced(WITH_CYCLES_DEVICE_CUDA)
|
||||
|
||||
option(WITH_CYCLES_CUDA_BINARIES "Build Cycles NVIDIA CUDA binaries" OFF)
|
||||
set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 compute_75 CACHE STRING "CUDA architectures to build binaries for")
|
||||
set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 sm_89 compute_75 CACHE STRING "CUDA architectures to build binaries for")
|
||||
option(WITH_CYCLES_CUDA_BUILD_SERIAL "Build cubins one after another (useful on machines with limited RAM)" OFF)
|
||||
option(WITH_CUDA_DYNLOAD "Dynamically load CUDA libraries at runtime (for developers, makes cuda-gdb work)" ON)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ set(EMBREE_EXTRA_ARGS
|
|||
-DEMBREE_BACKFACE_CULLING=OFF
|
||||
-DEMBREE_BACKFACE_CULLING_CURVES=ON
|
||||
-DEMBREE_BACKFACE_CULLING_SPHERES=ON
|
||||
-DEMBREE_NO_SPLASH=ON
|
||||
-DEMBREE_TASKING_SYSTEM=TBB
|
||||
-DEMBREE_TBB_ROOT=${LIBDIR}/tbb
|
||||
-DTBB_ROOT=${LIBDIR}/tbb
|
||||
|
|
|
@ -477,9 +477,9 @@ set(SQLITE_HASH_TYPE SHA1)
|
|||
set(SQLITE_FILE sqlite-autoconf-${SQLLITE_LONG_VERSION}.tar.gz)
|
||||
set(SQLITE_CPE "cpe:2.3:a:sqlite:sqlite:${SQLITE_VERSION}:*:*:*:*:*:*:*")
|
||||
|
||||
set(EMBREE_VERSION 4.0.1)
|
||||
set(EMBREE_VERSION 4.1.0)
|
||||
set(EMBREE_URI https://github.com/embree/embree/archive/v${EMBREE_VERSION}.zip)
|
||||
set(EMBREE_HASH dd26617719a587e126b341d1b32f7fd0)
|
||||
set(EMBREE_HASH 4b525955b08e1249a700dea5b5ffc8b2)
|
||||
set(EMBREE_HASH_TYPE MD5)
|
||||
set(EMBREE_FILE embree-v${EMBREE_VERSION}.zip)
|
||||
|
||||
|
|
|
@ -24,144 +24,3 @@ index 7c2f43d..106b1d5 100644
|
|||
DISABLE_STACK_PROTECTOR_FOR_INTERSECTORS(${EMBREE_LIBRARY_FILES_AVX2})
|
||||
ADD_LIBRARY(embree_avx2 STATIC ${EMBREE_LIBRARY_FILES_AVX2})
|
||||
TARGET_LINK_LIBRARIES(embree_avx2 PRIVATE tasking)
|
||||
diff --git a/include/embree4/rtcore_device.h b/include/embree4/rtcore_device.h
|
||||
index 45bf95583..62ee7787d 100644
|
||||
--- a/include/embree4/rtcore_device.h
|
||||
+++ b/include/embree4/rtcore_device.h
|
||||
@@ -55,6 +55,7 @@ enum RTCDeviceProperty
|
||||
RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED = 66,
|
||||
RTC_DEVICE_PROPERTY_IGNORE_INVALID_RAYS_ENABLED = 67,
|
||||
RTC_DEVICE_PROPERTY_COMPACT_POLYS_ENABLED = 68,
|
||||
+ RTC_DEVICE_PROPERTY_BACKFACE_CULLING_SPHERES_ENABLED = 69,
|
||||
|
||||
RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED = 96,
|
||||
RTC_DEVICE_PROPERTY_QUAD_GEOMETRY_SUPPORTED = 97,
|
||||
diff --git a/kernels/common/device.cpp b/kernels/common/device.cpp
|
||||
index 3ffac7e37..215ccc961 100644
|
||||
--- a/kernels/common/device.cpp
|
||||
+++ b/kernels/common/device.cpp
|
||||
@@ -170,6 +170,9 @@ namespace embree
|
||||
#if defined (EMBREE_BACKFACE_CULLING_CURVES)
|
||||
v += "backfacecullingcurves ";
|
||||
#endif
|
||||
+#if defined (EMBREE_BACKFACE_CULLING_SPHERES)
|
||||
+ v += "backfacecullingspheres ";
|
||||
+#endif
|
||||
#if defined(EMBREE_FILTER_FUNCTION)
|
||||
v += "intersection_filter ";
|
||||
#endif
|
||||
@@ -477,6 +480,12 @@ namespace embree
|
||||
case RTC_DEVICE_PROPERTY_BACKFACE_CULLING_CURVES_ENABLED: return 0;
|
||||
#endif
|
||||
|
||||
+#if defined(EMBREE_BACKFACE_CULLING_SPHERES)
|
||||
+ case RTC_DEVICE_PROPERTY_BACKFACE_CULLING_SPHERES_ENABLED: return 1;
|
||||
+#else
|
||||
+ case RTC_DEVICE_PROPERTY_BACKFACE_CULLING_SPHERES_ENABLED: return 0;
|
||||
+#endif
|
||||
+
|
||||
#if defined(EMBREE_COMPACT_POLYS)
|
||||
case RTC_DEVICE_PROPERTY_COMPACT_POLYS_ENABLED: return 1;
|
||||
#else
|
||||
diff --git a/kernels/config.h.in b/kernels/config.h.in
|
||||
index f02c90360..ba9acde56 100644
|
||||
--- a/kernels/config.h.in
|
||||
+++ b/kernels/config.h.in
|
||||
@@ -5,6 +5,7 @@
|
||||
#cmakedefine EMBREE_STAT_COUNTERS
|
||||
#cmakedefine EMBREE_BACKFACE_CULLING
|
||||
#cmakedefine EMBREE_BACKFACE_CULLING_CURVES
|
||||
+#cmakedefine EMBREE_BACKFACE_CULLING_SPHERES
|
||||
#cmakedefine EMBREE_FILTER_FUNCTION
|
||||
#cmakedefine EMBREE_IGNORE_INVALID_RAYS
|
||||
#cmakedefine EMBREE_GEOMETRY_TRIANGLE
|
||||
diff --git a/kernels/geometry/sphere_intersector.h b/kernels/geometry/sphere_intersector.h
|
||||
index 074f910a2..30f490818 100644
|
||||
--- a/kernels/geometry/sphere_intersector.h
|
||||
+++ b/kernels/geometry/sphere_intersector.h
|
||||
@@ -106,8 +106,13 @@ namespace embree
|
||||
const vbool<M> valid_front = valid & (ray.tnear() <= t_front) & (t_front <= ray.tfar);
|
||||
const vbool<M> valid_back = valid & (ray.tnear() <= t_back ) & (t_back <= ray.tfar);
|
||||
|
||||
+#if defined (EMBREE_BACKFACE_CULLING_SPHERES)
|
||||
+ /* check if there is a first hit */
|
||||
+ const vbool<M> valid_first = valid_front;
|
||||
+#else
|
||||
/* check if there is a first hit */
|
||||
const vbool<M> valid_first = valid_front | valid_back;
|
||||
+#endif
|
||||
if (unlikely(none(valid_first)))
|
||||
return false;
|
||||
|
||||
@@ -120,7 +125,8 @@ namespace embree
|
||||
|
||||
/* invoke intersection filter for first hit */
|
||||
const bool is_hit_first = epilog(valid_first, hit);
|
||||
-
|
||||
+
|
||||
+#if !defined (EMBREE_BACKFACE_CULLING_SPHERES)
|
||||
/* check for possible second hits before potentially accepted hit */
|
||||
const vfloat<M> t_second = t_back;
|
||||
const vbool<M> valid_second = valid_front & valid_back & (t_second <= ray.tfar);
|
||||
@@ -131,7 +137,9 @@ namespace embree
|
||||
const Vec3vf<M> Ng_second = td_back * ray_dir - perp;
|
||||
hit = SphereIntersectorHitM<M> (t_second, Ng_second);
|
||||
const bool is_hit_second = epilog(valid_second, hit);
|
||||
-
|
||||
+#else
|
||||
+ constexpr bool is_hit_second = false;
|
||||
+#endif
|
||||
return is_hit_first | is_hit_second;
|
||||
}
|
||||
|
||||
@@ -186,8 +194,13 @@ namespace embree
|
||||
const vbool<M> valid_front = valid & (ray.tnear()[k] <= t_front) & (t_front <= ray.tfar[k]);
|
||||
const vbool<M> valid_back = valid & (ray.tnear()[k] <= t_back ) & (t_back <= ray.tfar[k]);
|
||||
|
||||
+#if defined (EMBREE_BACKFACE_CULLING_SPHERES)
|
||||
+ /* check if there is a first hit */
|
||||
+ const vbool<M> valid_first = valid_front;
|
||||
+#else
|
||||
/* check if there is a first hit */
|
||||
const vbool<M> valid_first = valid_front | valid_back;
|
||||
+#endif
|
||||
if (unlikely(none(valid_first)))
|
||||
return false;
|
||||
|
||||
@@ -200,7 +213,8 @@ namespace embree
|
||||
|
||||
/* invoke intersection filter for first hit */
|
||||
const bool is_hit_first = epilog(valid_first, hit);
|
||||
-
|
||||
+
|
||||
+#if !defined (EMBREE_BACKFACE_CULLING_SPHERES)
|
||||
/* check for possible second hits before potentially accepted hit */
|
||||
const vfloat<M> t_second = t_back;
|
||||
const vbool<M> valid_second = valid_front & valid_back & (t_second <= ray.tfar[k]);
|
||||
@@ -211,7 +225,9 @@ namespace embree
|
||||
const Vec3vf<M> Ng_second = td_back * ray_dir - perp;
|
||||
hit = SphereIntersectorHitM<M> (t_second, Ng_second);
|
||||
const bool is_hit_second = epilog(valid_second, hit);
|
||||
-
|
||||
+#else
|
||||
+ constexpr bool is_hit_second = false;
|
||||
+#endif
|
||||
return is_hit_first | is_hit_second;
|
||||
}
|
||||
};
|
||||
diff -ruN a/kernels/sycl/rthwif_embree_builder.cpp b/kernels/sycl/rthwif_embree_builder.cpp
|
||||
--- a/kernels/sycl/rthwif_embree_builder.cpp 2023-03-28 17:23:06.429190200 +0200
|
||||
+++ b/kernels/sycl/rthwif_embree_builder.cpp 2023-03-28 17:35:01.291938600 +0200
|
||||
@@ -540,7 +540,12 @@
|
||||
assert(offset <= geomDescrData.size());
|
||||
}
|
||||
|
||||
+ /* Force running BVH building sequentially from the calling thread if using TBB < 2021, as it otherwise leads to runtime issues. */
|
||||
+#if TBB_VERSION_MAJOR<2021
|
||||
+ RTHWIF_PARALLEL_OPERATION parallelOperation = nullptr;
|
||||
+#else
|
||||
RTHWIF_PARALLEL_OPERATION parallelOperation = rthwifNewParallelOperation();
|
||||
+#endif
|
||||
|
||||
/* estimate static accel size */
|
||||
BBox1f time_range(0,1);
|
||||
|
|
|
@ -8,6 +8,8 @@ buildbot:
|
|||
version: '10.1.243'
|
||||
cuda11:
|
||||
version: '11.4.1'
|
||||
cuda12:
|
||||
version: '12.1.1'
|
||||
hip:
|
||||
version: '5.5.30571'
|
||||
hiprt:
|
||||
|
|
|
@ -302,10 +302,6 @@
|
|||
* \ingroup imbuf
|
||||
*/
|
||||
|
||||
/** \defgroup imbdds DDS
|
||||
* \ingroup imbuf
|
||||
*/
|
||||
|
||||
/** \defgroup openexr OpenEXR
|
||||
* \ingroup imbuf
|
||||
*/
|
||||
|
|
|
@ -389,7 +389,7 @@ static float4 LerpCurveSegmentMotionCV(ParticleCurveData *CData, int sys, int cu
|
|||
}
|
||||
const float4 mP = CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey);
|
||||
const float4 mP2 = CurveSegmentMotionCV(CData, sys, curve, first_curve_key + curvekey2);
|
||||
return lerp(mP, mP2, remainder);
|
||||
return mix(mP, mP2, remainder);
|
||||
}
|
||||
|
||||
static void export_hair_motion_validate_attribute(Hair *hair,
|
||||
|
@ -899,9 +899,9 @@ static float4 interpolate_curve_points(const float (*b_attr_position)[3],
|
|||
const int point_a = clamp((int)curve_t, 0, num_points - 1);
|
||||
const int point_b = min(point_a + 1, num_points - 1);
|
||||
const float t = curve_t - (float)point_a;
|
||||
return lerp(curve_point_as_float4(b_attr_position, b_attr_radius, first_point_index + point_a),
|
||||
curve_point_as_float4(b_attr_position, b_attr_radius, first_point_index + point_b),
|
||||
t);
|
||||
return mix(curve_point_as_float4(b_attr_position, b_attr_radius, first_point_index + point_a),
|
||||
curve_point_as_float4(b_attr_position, b_attr_radius, first_point_index + point_b),
|
||||
t);
|
||||
}
|
||||
|
||||
static void export_hair_curves(Scene *scene,
|
||||
|
|
|
@ -515,6 +515,7 @@ void BlenderSession::render_frame_finish()
|
|||
static bool bake_setup_pass(Scene *scene, const string &bake_type_str, const int bake_filter)
|
||||
{
|
||||
Integrator *integrator = scene->integrator;
|
||||
Film *film = scene->film;
|
||||
const char *bake_type = bake_type_str.c_str();
|
||||
|
||||
PassType type = PASS_NONE;
|
||||
|
@ -542,13 +543,29 @@ static bool bake_setup_pass(Scene *scene, const string &bake_type_str, const int
|
|||
else if (strcmp(bake_type, "ENVIRONMENT") == 0) {
|
||||
type = PASS_BACKGROUND;
|
||||
}
|
||||
/* AO passes. */
|
||||
/* AO pass. */
|
||||
else if (strcmp(bake_type, "AO") == 0) {
|
||||
type = PASS_AO;
|
||||
}
|
||||
/* Shadow pass. */
|
||||
else if (strcmp(bake_type, "SHADOW") == 0) {
|
||||
/* Bake as combined pass, together with marking the object as a shadow catcher. */
|
||||
type = PASS_SHADOW_CATCHER;
|
||||
film->set_use_approximate_shadow_catcher(true);
|
||||
|
||||
use_direct_light = true;
|
||||
use_indirect_light = true;
|
||||
include_albedo = true;
|
||||
|
||||
integrator->set_use_diffuse(true);
|
||||
integrator->set_use_glossy(true);
|
||||
integrator->set_use_transmission(true);
|
||||
integrator->set_use_emission(true);
|
||||
}
|
||||
/* Combined pass. */
|
||||
else if (strcmp(bake_type, "COMBINED") == 0) {
|
||||
type = PASS_COMBINED;
|
||||
film->set_use_approximate_shadow_catcher(true);
|
||||
|
||||
use_direct_light = (bake_filter & BL::BakeSettings::pass_filter_DIRECT) != 0;
|
||||
use_indirect_light = (bake_filter & BL::BakeSettings::pass_filter_INDIRECT) != 0;
|
||||
|
@ -683,17 +700,23 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
|
|||
|
||||
/* Object might have been disabled for rendering or excluded in some
|
||||
* other way, in that case Blender will report a warning afterwards. */
|
||||
bool object_found = false;
|
||||
Object *bake_object = nullptr;
|
||||
if (!session->progress.get_cancel()) {
|
||||
foreach (Object *ob, scene->objects) {
|
||||
if (ob->name == b_object.name()) {
|
||||
object_found = true;
|
||||
bake_object = ob;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (object_found && !session->progress.get_cancel()) {
|
||||
/* For the shadow pass, temporarily mark the object as a shadow catcher. */
|
||||
const bool was_shadow_catcher = (bake_object) ? bake_object->get_is_shadow_catcher() : false;
|
||||
if (bake_object && bake_type == "SHADOW") {
|
||||
bake_object->set_is_shadow_catcher(true);
|
||||
}
|
||||
|
||||
if (bake_object && !session->progress.get_cancel()) {
|
||||
/* Get session and buffer parameters. */
|
||||
const SessionParams session_params = BlenderSync::get_session_params(
|
||||
b_engine, b_userpref, b_scene, background);
|
||||
|
@ -714,10 +737,15 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
|
|||
}
|
||||
|
||||
/* Perform bake. Check cancel to avoid crash with incomplete scene data. */
|
||||
if (object_found && !session->progress.get_cancel()) {
|
||||
if (bake_object && !session->progress.get_cancel()) {
|
||||
session->start();
|
||||
session->wait();
|
||||
}
|
||||
|
||||
/* Restore object state. */
|
||||
if (bake_object) {
|
||||
bake_object->set_is_shadow_catcher(was_shadow_catcher);
|
||||
}
|
||||
}
|
||||
|
||||
void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
|
||||
|
|
|
@ -360,7 +360,7 @@ void BVHSpatialSplit::split_triangle_primitive(const Mesh *mesh,
|
|||
|
||||
/* edge intersects the plane => insert intersection to both boxes. */
|
||||
if ((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
|
||||
float3 t = lerp(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
|
||||
float3 t = mix(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
|
||||
left_bounds.grow(t);
|
||||
right_bounds.grow(t);
|
||||
}
|
||||
|
@ -408,7 +408,7 @@ void BVHSpatialSplit::split_curve_primitive(const Hair *hair,
|
|||
|
||||
/* edge intersects the plane => insert intersection to both boxes. */
|
||||
if ((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
|
||||
float3 t = lerp(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
|
||||
float3 t = mix(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
|
||||
left_bounds.grow(t);
|
||||
right_bounds.grow(t);
|
||||
}
|
||||
|
|
|
@ -122,8 +122,13 @@ bool OneapiDevice::check_peer_access(Device * /*peer_device*/)
|
|||
|
||||
bool OneapiDevice::can_use_hardware_raytracing_for_features(uint requested_features) const
|
||||
{
|
||||
/* MNEE and Ray-trace kernels currently don't work correctly with HWRT. */
|
||||
/* MNEE and Raytrace kernels work correctly with Hardware Raytracing starting with Embree 4.1. */
|
||||
# if defined(RTC_VERSION) && RTC_VERSION < 40100
|
||||
return !(requested_features & (KERNEL_FEATURE_MNEE | KERNEL_FEATURE_NODE_RAYTRACE));
|
||||
# else
|
||||
(void)requested_features;
|
||||
return true;
|
||||
# endif
|
||||
}
|
||||
|
||||
BVHLayoutMask OneapiDevice::get_bvh_layout_mask(uint requested_features) const
|
||||
|
|
|
@ -62,7 +62,7 @@ bool work_balance_do_rebalance(vector<WorkBalanceInfo> &work_balance_infos)
|
|||
bool has_big_difference = false;
|
||||
|
||||
for (const WorkBalanceInfo &info : work_balance_infos) {
|
||||
const double time_target = lerp(info.time_spent, time_average, lerp_weight);
|
||||
const double time_target = mix(info.time_spent, time_average, lerp_weight);
|
||||
const double new_weight = info.weight * time_target / info.time_spent;
|
||||
new_weights.push_back(new_weight);
|
||||
total_weight += new_weight;
|
||||
|
|
|
@ -540,12 +540,12 @@ if(WITH_CYCLES_CUDA_BINARIES)
|
|||
elseif(${arch} MATCHES ".*_7." AND "${CUDA_VERSION}" LESS 100)
|
||||
message(STATUS "CUDA binaries for ${arch} require CUDA 10.0+, skipped.")
|
||||
elseif(${arch} MATCHES ".*_8.")
|
||||
if(DEFINED CUDA11_NVCC_EXECUTABLE)
|
||||
set(cuda_nvcc_executable ${CUDA11_NVCC_EXECUTABLE})
|
||||
set(cuda_toolkit_root_dir ${CUDA11_TOOLKIT_ROOT_DIR})
|
||||
elseif("${CUDA_VERSION}" GREATER_EQUAL 111) # Support for sm_86 was introduced in CUDA 11
|
||||
if("${CUDA_VERSION}" GREATER_EQUAL 111) # Support for sm_86 was introduced in CUDA 11
|
||||
set(cuda_nvcc_executable ${CUDA_NVCC_EXECUTABLE})
|
||||
set(cuda_toolkit_root_dir ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
elseif(DEFINED CUDA11_NVCC_EXECUTABLE)
|
||||
set(cuda_nvcc_executable ${CUDA11_NVCC_EXECUTABLE})
|
||||
set(cuda_toolkit_root_dir ${CUDA11_TOOLKIT_ROOT_DIR})
|
||||
else()
|
||||
message(STATUS "CUDA binaries for ${arch} require CUDA 11.1+, skipped.")
|
||||
endif()
|
||||
|
|
|
@ -96,7 +96,7 @@ ccl_device_forceinline Spectrum interpolate_fresnel_color(float3 L,
|
|||
{
|
||||
/* Compute the real Fresnel term and remap it from real_F0..1 to F0..1.
|
||||
* The reason why we use this remapping instead of directly doing the
|
||||
* Schlick approximation lerp(F0, 1.0, (1.0-cosLH)^5) is that for cases
|
||||
* Schlick approximation mix(F0, 1.0, (1.0-cosLH)^5) is that for cases
|
||||
* with similar IORs (e.g. ice in water), the relative IOR can be close
|
||||
* enough to 1.0 that the Schlick approximation becomes inaccurate. */
|
||||
float real_F = fresnel_dielectric_cos(dot(L, H), ior);
|
||||
|
|
|
@ -174,27 +174,30 @@ bool oneapi_kernel_is_required_for_features(const std::string &kernel_name,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool oneapi_kernel_is_raytrace_or_mnee(const std::string &kernel_name)
|
||||
bool oneapi_kernel_is_compatible_with_hardware_raytracing(const std::string &kernel_name)
|
||||
{
|
||||
return (kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE)) !=
|
||||
std::string::npos) ||
|
||||
/* MNEE and Raytrace kernels work correctly with Hardware Raytracing starting with Embree 4.1.
|
||||
*/
|
||||
# if defined(RTC_VERSION) && RTC_VERSION < 40100
|
||||
return (kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE)) ==
|
||||
std::string::npos) &&
|
||||
(kernel_name.find(device_kernel_as_string(
|
||||
DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE)) != std::string::npos);
|
||||
DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE)) == std::string::npos);
|
||||
# else
|
||||
return true;
|
||||
# endif
|
||||
}
|
||||
|
||||
bool oneapi_kernel_is_using_embree(const std::string &kernel_name)
|
||||
bool oneapi_kernel_has_intersections(const std::string &kernel_name)
|
||||
{
|
||||
# ifdef WITH_EMBREE_GPU
|
||||
/* MNEE and Ray-trace kernels aren't yet enabled to use Embree. */
|
||||
for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) {
|
||||
DeviceKernel kernel = (DeviceKernel)i;
|
||||
if (device_kernel_has_intersection(kernel)) {
|
||||
if (kernel_name.find(device_kernel_as_string(kernel)) != std::string::npos) {
|
||||
return !oneapi_kernel_is_raytrace_or_mnee(kernel_name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -217,7 +220,8 @@ bool oneapi_load_kernels(SyclQueue *queue_,
|
|||
const std::string &kernel_name = kernel_id.get_name();
|
||||
|
||||
if (!oneapi_kernel_is_required_for_features(kernel_name, kernel_features) ||
|
||||
!oneapi_kernel_is_using_embree(kernel_name))
|
||||
!(oneapi_kernel_has_intersections(kernel_name) &&
|
||||
oneapi_kernel_is_compatible_with_hardware_raytracing(kernel_name)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -260,14 +264,14 @@ bool oneapi_load_kernels(SyclQueue *queue_,
|
|||
/* In case HWRT is on, compilation of kernels using Embree is already handled in previous
|
||||
* block. */
|
||||
if (!oneapi_kernel_is_required_for_features(kernel_name, kernel_features) ||
|
||||
(use_hardware_raytracing && oneapi_kernel_is_using_embree(kernel_name)))
|
||||
(use_hardware_raytracing && oneapi_kernel_has_intersections(kernel_name) &&
|
||||
oneapi_kernel_is_compatible_with_hardware_raytracing(kernel_name)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
# ifdef WITH_EMBREE_GPU
|
||||
if (oneapi_kernel_is_using_embree(kernel_name) ||
|
||||
oneapi_kernel_is_raytrace_or_mnee(kernel_name)) {
|
||||
if (oneapi_kernel_has_intersections(kernel_name)) {
|
||||
sycl::kernel_bundle<sycl::bundle_state::input> one_kernel_bundle_input =
|
||||
sycl::get_kernel_bundle<sycl::bundle_state::input>(queue->get_context(), {kernel_id});
|
||||
one_kernel_bundle_input
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "kernel/film/adaptive_sampling.h"
|
||||
#include "kernel/film/light_passes.h"
|
||||
|
||||
#include "kernel/integrator/intersect_closest.h"
|
||||
#include "kernel/integrator/path_state.h"
|
||||
|
||||
#include "kernel/sample/pattern.h"
|
||||
|
@ -327,6 +328,8 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg,
|
|||
else {
|
||||
integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader_index);
|
||||
}
|
||||
|
||||
integrator_split_shadow_catcher(kg, state, &isect, render_buffer);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -398,6 +398,7 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg,
|
|||
ccl_private const ShaderData *sd,
|
||||
ccl_private ShaderData *sd_vtx,
|
||||
ccl_private const LightSample *ls,
|
||||
const bool light_fixed_direction,
|
||||
int vertex_count,
|
||||
ccl_private ManifoldVertex *vertices)
|
||||
{
|
||||
|
@ -413,7 +414,6 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg,
|
|||
projection_ray.time = sd->time;
|
||||
Intersection projection_isect;
|
||||
|
||||
const bool light_fixed_direction = (ls->t == FLT_MAX);
|
||||
const float3 light_sample = light_fixed_direction ? ls->D : ls->P;
|
||||
|
||||
/* We start gently, potentially ramping up to beta = 1, since target configurations
|
||||
|
@ -639,6 +639,7 @@ ccl_device_forceinline Spectrum mnee_eval_bsdf_contribution(ccl_private ShaderCl
|
|||
/* Compute transfer matrix determinant |T1| = |dx1/dxn| (and |dh/dx| in the process) */
|
||||
ccl_device_forceinline bool mnee_compute_transfer_matrix(ccl_private const ShaderData *sd,
|
||||
ccl_private const LightSample *ls,
|
||||
const bool light_fixed_direction,
|
||||
int vertex_count,
|
||||
ccl_private ManifoldVertex *vertices,
|
||||
ccl_private float *dx1_dxlight,
|
||||
|
@ -692,7 +693,7 @@ ccl_device_forceinline bool mnee_compute_transfer_matrix(ccl_private const Shade
|
|||
float dxn_dwn;
|
||||
float4 dc_dlight;
|
||||
|
||||
if (ls->t == FLT_MAX) {
|
||||
if (light_fixed_direction) {
|
||||
/* Constant direction toward light sample. */
|
||||
float3 wo = ls->D;
|
||||
|
||||
|
@ -764,6 +765,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
|
|||
ccl_private ShaderData *sd,
|
||||
ccl_private ShaderData *sd_mnee,
|
||||
ccl_private LightSample *ls,
|
||||
const bool light_fixed_direction,
|
||||
int vertex_count,
|
||||
ccl_private ManifoldVertex *vertices,
|
||||
ccl_private BsdfEval *throughput)
|
||||
|
@ -807,7 +809,8 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
|
|||
|
||||
float dh_dx;
|
||||
float dx1_dxlight;
|
||||
if (!mnee_compute_transfer_matrix(sd, ls, vertex_count, vertices, &dx1_dxlight, &dh_dx))
|
||||
if (!mnee_compute_transfer_matrix(
|
||||
sd, ls, light_fixed_direction, vertex_count, vertices, &dx1_dxlight, &dh_dx))
|
||||
return false;
|
||||
|
||||
/* Receiver bsdf eval above already contains |n.wo|. */
|
||||
|
@ -882,7 +885,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
|
|||
kg, state, sd_mnee, NULL, PATH_RAY_DIFFUSE, true);
|
||||
|
||||
/* Set light looking dir. */
|
||||
wo = (vi == vertex_count - 1) ? (ls->t == FLT_MAX ? ls->D : ls->P - v.p) :
|
||||
wo = (vi == vertex_count - 1) ? (light_fixed_direction ? ls->D : ls->P - v.p) :
|
||||
vertices[vi + 1].p - v.p;
|
||||
wo = normalize_len(wo, &wo_len);
|
||||
|
||||
|
@ -1039,12 +1042,22 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg,
|
|||
* discontinuity is visible between direct and indirect contributions */
|
||||
INTEGRATOR_STATE_WRITE(state, path, mnee) |= PATH_MNEE_VALID;
|
||||
|
||||
/* 2. Walk on the specular manifold to find vertices on the
|
||||
* casters that satisfy snell's law for each interface
|
||||
*/
|
||||
if (mnee_newton_solver(kg, sd, sd_mnee, ls, vertex_count, vertices)) {
|
||||
/* Distant or environment light. */
|
||||
bool light_fixed_direction = (ls->t == FLT_MAX);
|
||||
if (ls->type == LIGHT_AREA) {
|
||||
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
|
||||
if (klight->area.tan_half_spread == 0.0f) {
|
||||
/* Area light with zero spread also has fixed direction. */
|
||||
light_fixed_direction = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. Walk on the specular manifold to find vertices on the casters that satisfy snell's law for
|
||||
* each interface. */
|
||||
if (mnee_newton_solver(kg, sd, sd_mnee, ls, light_fixed_direction, vertex_count, vertices)) {
|
||||
/* 3. If a solution exists, calculate contribution of the corresponding path */
|
||||
if (!mnee_path_contribution(kg, state, sd, sd_mnee, ls, vertex_count, vertices, throughput))
|
||||
if (!mnee_path_contribution(
|
||||
kg, state, sd, sd_mnee, ls, light_fixed_direction, vertex_count, vertices, throughput))
|
||||
return 0;
|
||||
|
||||
return vertex_count;
|
||||
|
|
|
@ -227,55 +227,43 @@ ccl_device bool area_light_spread_clamp_light(const float3 P,
|
|||
}
|
||||
|
||||
/* Common API. */
|
||||
|
||||
/* Compute `eval_fac` and `pdf`. Also sample a new position on the light if `sample_coord`. */
|
||||
template<bool in_volume_segment>
|
||||
ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
|
||||
const float randu,
|
||||
const float randv,
|
||||
const float3 P,
|
||||
ccl_private LightSample *ls)
|
||||
ccl_device_inline bool area_light_eval(const ccl_global KernelLight *klight,
|
||||
const float3 ray_P,
|
||||
ccl_private float3 *light_P,
|
||||
ccl_private LightSample *ccl_restrict ls,
|
||||
float randu,
|
||||
float randv,
|
||||
bool sample_coord)
|
||||
{
|
||||
ls->P = klight->co;
|
||||
float3 axis_u = klight->area.axis_u;
|
||||
float3 axis_v = klight->area.axis_v;
|
||||
float len_u = klight->area.len_u;
|
||||
float len_v = klight->area.len_v;
|
||||
|
||||
float3 Ng = klight->area.dir;
|
||||
const float3 Ng = klight->area.dir;
|
||||
const float invarea = fabsf(klight->area.invarea);
|
||||
bool sample_rectangle = (klight->area.invarea > 0.0f);
|
||||
|
||||
if (!in_volume_segment) {
|
||||
if (dot(ls->P - P, Ng) > 0.0f) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const float3 axis_u = klight->area.axis_u;
|
||||
const float3 axis_v = klight->area.axis_v;
|
||||
const float len_u = klight->area.len_u;
|
||||
const float len_v = klight->area.len_v;
|
||||
float invarea = fabsf(klight->area.invarea);
|
||||
bool is_ellipse = (klight->area.invarea < 0.0f);
|
||||
bool sample_rectangle = !is_ellipse;
|
||||
float3 inplane;
|
||||
float3 light_P_new = *light_P;
|
||||
|
||||
if (in_volume_segment) {
|
||||
inplane = sample_rectangle ?
|
||||
rectangle_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, randu, randv) :
|
||||
ellipse_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, randu, randv);
|
||||
ls->P += inplane;
|
||||
light_P_new += sample_rectangle ?
|
||||
rectangle_sample(
|
||||
axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, randu, randv) :
|
||||
ellipse_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, randu, randv);
|
||||
ls->pdf = invarea;
|
||||
}
|
||||
else {
|
||||
float3 old_P = ls->P;
|
||||
float3 sample_axis_u = axis_u;
|
||||
float3 sample_axis_v = axis_v;
|
||||
float sample_len_u = len_u;
|
||||
float sample_len_v = len_v;
|
||||
|
||||
if (klight->area.normalize_spread > 0) {
|
||||
if (!area_light_spread_clamp_light(P,
|
||||
if (!area_light_spread_clamp_light(ray_P,
|
||||
Ng,
|
||||
&ls->P,
|
||||
&sample_axis_u,
|
||||
&sample_len_u,
|
||||
&sample_axis_v,
|
||||
&sample_len_v,
|
||||
&light_P_new,
|
||||
&axis_u,
|
||||
&len_u,
|
||||
&axis_v,
|
||||
&len_v,
|
||||
klight->area.tan_half_spread,
|
||||
&sample_rectangle))
|
||||
{
|
||||
|
@ -285,53 +273,82 @@ ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
|
|||
|
||||
if (sample_rectangle) {
|
||||
ls->pdf = area_light_rect_sample(
|
||||
P, &ls->P, sample_axis_u, sample_len_u, sample_axis_v, sample_len_v, randu, randv, true);
|
||||
ray_P, &light_P_new, axis_u, len_u, axis_v, len_v, randu, randv, sample_coord);
|
||||
}
|
||||
else {
|
||||
if (klight->area.tan_half_spread == 0.0f) {
|
||||
ls->pdf = 1.0f;
|
||||
}
|
||||
else {
|
||||
ls->P += ellipse_sample(sample_axis_u * sample_len_u * 0.5f,
|
||||
sample_axis_v * sample_len_v * 0.5f,
|
||||
randu,
|
||||
randv);
|
||||
ls->pdf = 4.0f * M_1_PI_F / (sample_len_u * sample_len_v);
|
||||
if (sample_coord) {
|
||||
light_P_new += ellipse_sample(
|
||||
axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, randu, randv);
|
||||
}
|
||||
ls->pdf = 4.0f * M_1_PI_F / (len_u * len_v);
|
||||
}
|
||||
}
|
||||
inplane = ls->P - old_P;
|
||||
}
|
||||
|
||||
const float light_u = dot(inplane, axis_u) / len_u;
|
||||
const float light_v = dot(inplane, axis_v) / len_v;
|
||||
|
||||
/* Sampled point lies outside of the area light. */
|
||||
if (is_ellipse && (sqr(light_u) + sqr(light_v) > 0.25f)) {
|
||||
return false;
|
||||
if (sample_coord) {
|
||||
*light_P = light_P_new;
|
||||
ls->D = normalize_len(*light_P - ray_P, &ls->t);
|
||||
}
|
||||
if (!is_ellipse && (fabsf(light_u) > 0.5f || fabsf(light_v) > 0.5f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
|
||||
ls->u = light_v + 0.5f;
|
||||
ls->v = -light_u - light_v;
|
||||
|
||||
ls->Ng = Ng;
|
||||
ls->D = normalize_len(ls->P - P, &ls->t);
|
||||
|
||||
ls->eval_fac = 0.25f * invarea;
|
||||
|
||||
if (klight->area.normalize_spread > 0) {
|
||||
/* Area Light spread angle attenuation */
|
||||
ls->eval_fac *= area_light_spread_attenuation(
|
||||
ls->D, ls->Ng, klight->area.tan_half_spread, klight->area.normalize_spread);
|
||||
ls->D, Ng, klight->area.tan_half_spread, klight->area.normalize_spread);
|
||||
}
|
||||
|
||||
if (!sample_rectangle && klight->area.tan_half_spread > 0) {
|
||||
if (in_volume_segment || (!sample_rectangle && klight->area.tan_half_spread > 0)) {
|
||||
ls->pdf *= lamp_light_pdf(Ng, -ls->D, ls->t);
|
||||
}
|
||||
|
||||
return ls->eval_fac > 0;
|
||||
}
|
||||
|
||||
template<bool in_volume_segment>
|
||||
ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
|
||||
const float randu,
|
||||
const float randv,
|
||||
const float3 P,
|
||||
ccl_private LightSample *ls)
|
||||
{
|
||||
ls->P = klight->co;
|
||||
ls->Ng = klight->area.dir;
|
||||
|
||||
if (!in_volume_segment) {
|
||||
if (dot(ls->P - P, ls->Ng) > 0.0f) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!area_light_eval<in_volume_segment>(klight, P, &ls->P, ls, randu, randv, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float3 inplane = ls->P - klight->co;
|
||||
const float light_u = dot(inplane, klight->area.axis_u) / klight->area.len_u;
|
||||
const float light_v = dot(inplane, klight->area.axis_v) / klight->area.len_v;
|
||||
|
||||
if (!in_volume_segment) {
|
||||
const bool is_ellipse = (klight->area.invarea < 0.0f);
|
||||
|
||||
/* Sampled point lies outside of the area light. */
|
||||
if (is_ellipse && (sqr(light_u) + sqr(light_v) > 0.25f)) {
|
||||
return false;
|
||||
}
|
||||
if (!is_ellipse && (fabsf(light_u) > 0.5f || fabsf(light_v) > 0.5f)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
|
||||
ls->u = light_v + 0.5f;
|
||||
ls->v = -light_u - light_v;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -339,14 +356,15 @@ ccl_device_forceinline void area_light_update_position(const ccl_global KernelLi
|
|||
ccl_private LightSample *ls,
|
||||
const float3 P)
|
||||
{
|
||||
const float invarea = fabsf(klight->area.invarea);
|
||||
ls->D = normalize_len(ls->P - P, &ls->t);
|
||||
ls->pdf = invarea;
|
||||
|
||||
if (klight->area.normalize_spread > 0) {
|
||||
ls->eval_fac = 0.25f * invarea;
|
||||
ls->eval_fac *= area_light_spread_attenuation(
|
||||
ls->D, ls->Ng, klight->area.tan_half_spread, klight->area.normalize_spread);
|
||||
if (klight->area.tan_half_spread == 0) {
|
||||
/* Update position on the light to keep the direction fixed. */
|
||||
area_light_eval<false>(klight, P, &ls->P, ls, 0, 0, true);
|
||||
}
|
||||
else {
|
||||
ls->D = normalize_len(ls->P - P, &ls->t);
|
||||
area_light_eval<false>(klight, P, &ls->P, ls, 0, 0, false);
|
||||
/* Convert pdf to be in area measure. */
|
||||
ls->pdf /= lamp_light_pdf(ls->Ng, -ls->D, ls->t);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -397,60 +415,13 @@ ccl_device_inline bool area_light_sample_from_intersection(
|
|||
const float3 ray_D,
|
||||
ccl_private LightSample *ccl_restrict ls)
|
||||
{
|
||||
|
||||
/* area light */
|
||||
float invarea = fabsf(klight->area.invarea);
|
||||
|
||||
float3 Ng = klight->area.dir;
|
||||
float3 light_P = klight->co;
|
||||
|
||||
ls->u = isect->u;
|
||||
ls->v = isect->v;
|
||||
ls->D = ray_D;
|
||||
ls->Ng = Ng;
|
||||
ls->Ng = klight->area.dir;
|
||||
|
||||
float3 sample_axis_u = klight->area.axis_u;
|
||||
float3 sample_axis_v = klight->area.axis_v;
|
||||
float sample_len_u = klight->area.len_u;
|
||||
float sample_len_v = klight->area.len_v;
|
||||
bool is_ellipse = (klight->area.invarea < 0.0f);
|
||||
bool sample_rectangle = !is_ellipse;
|
||||
|
||||
if (klight->area.normalize_spread > 0) {
|
||||
if (!area_light_spread_clamp_light(ray_P,
|
||||
Ng,
|
||||
&light_P,
|
||||
&sample_axis_u,
|
||||
&sample_len_u,
|
||||
&sample_axis_v,
|
||||
&sample_len_v,
|
||||
klight->area.tan_half_spread,
|
||||
&sample_rectangle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (sample_rectangle) {
|
||||
ls->pdf = area_light_rect_sample(
|
||||
ray_P, &light_P, sample_axis_u, sample_len_u, sample_axis_v, sample_len_v, 0, 0, false);
|
||||
}
|
||||
else {
|
||||
ls->pdf = klight->area.tan_half_spread == 0.0f ?
|
||||
1.0f :
|
||||
4.0f * M_1_PI_F / (sample_len_u * sample_len_v) *
|
||||
lamp_light_pdf(Ng, -ray_D, ls->t);
|
||||
}
|
||||
|
||||
ls->eval_fac = 0.25f * invarea;
|
||||
|
||||
if (klight->area.normalize_spread > 0) {
|
||||
/* Area Light spread angle attenuation */
|
||||
ls->eval_fac *= area_light_spread_attenuation(
|
||||
ls->D, ls->Ng, klight->area.tan_half_spread, klight->area.normalize_spread);
|
||||
}
|
||||
|
||||
return ls->eval_fac > 0;
|
||||
float3 light_P = klight->co;
|
||||
return area_light_eval<false>(klight, ray_P, &light_P, ls, 0, 0, false);
|
||||
}
|
||||
|
||||
template<bool in_volume_segment>
|
||||
|
|
|
@ -36,7 +36,7 @@ static float shutter_curve_eval(float x, array<float> &shutter_curve)
|
|||
int index = (int)x;
|
||||
float frac = x - index;
|
||||
if (index < shutter_curve.size() - 1) {
|
||||
return lerp(shutter_curve[index], shutter_curve[index + 1], frac);
|
||||
return mix(shutter_curve[index], shutter_curve[index + 1], frac);
|
||||
}
|
||||
else {
|
||||
return shutter_curve[shutter_curve.size() - 1];
|
||||
|
|
|
@ -1291,7 +1291,7 @@ template<class T> void init_test_curve(array<T> &buffer, T start, T end, int ste
|
|||
buffer.resize(steps);
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
buffer[i] = lerp(start, end, float(i) / (steps - 1));
|
||||
buffer[i] = mix(start, end, float(i) / (steps - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -555,16 +555,6 @@ CCL_NAMESPACE_END
|
|||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#if !defined(__KERNEL_METAL__)
|
||||
/* Interpolation */
|
||||
|
||||
template<class A, class B> A lerp(const A &a, const A &b, const B &t)
|
||||
{
|
||||
return (A)(a * ((B)1 - t) + b * t);
|
||||
}
|
||||
|
||||
#endif /* __KERNEL_METAL__ */
|
||||
|
||||
/* Triangle */
|
||||
|
||||
ccl_device_inline float triangle_area(ccl_private const float3 &v1,
|
||||
|
|
|
@ -379,7 +379,7 @@ ccl_device_inline Transform transform_empty()
|
|||
|
||||
ccl_device_inline float4 quat_interpolate(float4 q1, float4 q2, float t)
|
||||
{
|
||||
/* Optix and MetalRT are using lerp to interpolate motion transformations. */
|
||||
/* Optix and MetalRT are using linear interpolation to interpolate motion transformations. */
|
||||
#if defined(__KERNEL_GPU_RAYTRACING__)
|
||||
return normalize((1.0f - t) * q1 + t * q2);
|
||||
#else /* defined(__KERNEL_GPU_RAYTRACING__) */
|
||||
|
|
|
@ -20,7 +20,7 @@ from bpy.app.translations import pgettext_tip as tip_
|
|||
|
||||
|
||||
class ANIM_OT_keying_set_export(Operator):
|
||||
"""Export Keying Set to a python script"""
|
||||
"""Export Keying Set to a Python script"""
|
||||
bl_idname = "anim.keying_set_export"
|
||||
bl_label = "Export Keying Set..."
|
||||
|
||||
|
@ -38,7 +38,7 @@ class ANIM_OT_keying_set_export(Operator):
|
|||
options={'HIDDEN'},
|
||||
)
|
||||
filter_python: BoolProperty(
|
||||
name="Filter python",
|
||||
name="Filter Python",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
|
|
@ -16,7 +16,7 @@ def _lang_module_get(sc):
|
|||
|
||||
|
||||
class ConsoleExec(Operator):
|
||||
"""Execute the current console line as a python expression"""
|
||||
"""Execute the current console line as a Python expression"""
|
||||
bl_idname = "console.execute"
|
||||
bl_label = "Console Execute"
|
||||
bl_options = {'UNDO_GROUPED'}
|
||||
|
|
|
@ -78,7 +78,7 @@ class WM_OT_previews_batch_generate(Operator):
|
|||
use_trusted: BoolProperty(
|
||||
default=False,
|
||||
name="Trusted Blend Files",
|
||||
description="Enable python evaluation for selected files",
|
||||
description="Enable Python evaluation for selected files",
|
||||
)
|
||||
use_backups: BoolProperty(
|
||||
default=True,
|
||||
|
@ -188,7 +188,7 @@ class WM_OT_previews_batch_clear(Operator):
|
|||
use_trusted: BoolProperty(
|
||||
default=False,
|
||||
name="Trusted Blend Files",
|
||||
description="Enable python evaluation for selected files",
|
||||
description="Enable Python evaluation for selected files",
|
||||
)
|
||||
use_backups: BoolProperty(
|
||||
default=True,
|
||||
|
|
|
@ -173,7 +173,7 @@ class PREFERENCES_OT_keyconfig_test(Operator):
|
|||
|
||||
|
||||
class PREFERENCES_OT_keyconfig_import(Operator):
|
||||
"""Import key configuration from a python script"""
|
||||
"""Import key configuration from a Python script"""
|
||||
bl_idname = "preferences.keyconfig_import"
|
||||
bl_label = "Import Key Configuration..."
|
||||
|
||||
|
@ -192,7 +192,7 @@ class PREFERENCES_OT_keyconfig_import(Operator):
|
|||
options={'HIDDEN'},
|
||||
)
|
||||
filter_python: BoolProperty(
|
||||
name="Filter python",
|
||||
name="Filter Python",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
@ -244,7 +244,7 @@ class PREFERENCES_OT_keyconfig_import(Operator):
|
|||
|
||||
|
||||
class PREFERENCES_OT_keyconfig_export(Operator):
|
||||
"""Export key configuration to a python script"""
|
||||
"""Export key configuration to a Python script"""
|
||||
bl_idname = "preferences.keyconfig_export"
|
||||
bl_label = "Export Key Configuration..."
|
||||
|
||||
|
@ -268,7 +268,7 @@ class PREFERENCES_OT_keyconfig_export(Operator):
|
|||
options={'HIDDEN'},
|
||||
)
|
||||
filter_python: BoolProperty(
|
||||
name="Filter python",
|
||||
name="Filter Python",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
@ -610,7 +610,7 @@ class PREFERENCES_OT_addon_install(Operator):
|
|||
options={'HIDDEN'},
|
||||
)
|
||||
filter_python: BoolProperty(
|
||||
name="Filter python",
|
||||
name="Filter Python",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
|
|
@ -746,7 +746,7 @@ class WM_OT_operator_pie_enum(Operator):
|
|||
|
||||
data_path: StringProperty(
|
||||
name="Operator",
|
||||
description="Operator name (in python as string)",
|
||||
description="Operator name (in Python as string)",
|
||||
maxlen=1024,
|
||||
)
|
||||
prop_string: StringProperty(
|
||||
|
@ -1018,9 +1018,44 @@ class WM_OT_url_open(Operator):
|
|||
description="URL to open",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _add_utm_param_to_url(url, utm_source):
|
||||
import urllib
|
||||
|
||||
# Make sure we have a scheme otherwise we can't parse the url.
|
||||
if not url.startswith(("http://", "https://")):
|
||||
url = "https://" + url
|
||||
|
||||
# Parse the URL to get its domain and query parameters.
|
||||
parsed_url = urllib.parse.urlparse(url)
|
||||
domain = parsed_url.netloc
|
||||
|
||||
# Only add a utm source if it points to a blender.org domain.
|
||||
if not (domain.endswith(".blender.org") or domain == "blender.org"):
|
||||
return url
|
||||
|
||||
# Parse the query parameters and add or update the utm_source parameter.
|
||||
query_params = urllib.parse.parse_qs(parsed_url.query)
|
||||
query_params["utm_source"] = utm_source
|
||||
new_query = urllib.parse.urlencode(query_params, doseq=True)
|
||||
|
||||
# Create a new URL with the updated query parameters.
|
||||
new_url_parts = list(parsed_url)
|
||||
new_url_parts[4] = new_query
|
||||
new_url = urllib.parse.urlunparse(new_url_parts)
|
||||
|
||||
return new_url
|
||||
|
||||
@staticmethod
|
||||
def _get_utm_source():
|
||||
version = bpy.app.version_string
|
||||
formatted_version = version.replace(' ', '-').lower()
|
||||
return f"blender-{formatted_version}"
|
||||
|
||||
def execute(self, _context):
|
||||
import webbrowser
|
||||
webbrowser.open(self.url)
|
||||
complete_url = self._add_utm_param_to_url(self.url, self._get_utm_source())
|
||||
webbrowser.open(complete_url)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
@ -1102,10 +1137,7 @@ class WM_OT_url_open_preset(Operator):
|
|||
url = url(self, context)
|
||||
break
|
||||
|
||||
import webbrowser
|
||||
webbrowser.open(url)
|
||||
|
||||
return {'FINISHED'}
|
||||
return bpy.ops.wm.url_open(url=url)
|
||||
|
||||
|
||||
class WM_OT_path_open(Operator):
|
||||
|
@ -1307,9 +1339,7 @@ class WM_OT_doc_view_manual(Operator):
|
|||
)
|
||||
return {'CANCELLED'}
|
||||
else:
|
||||
import webbrowser
|
||||
webbrowser.open(url)
|
||||
return {'FINISHED'}
|
||||
return bpy.ops.wm.url_open(url=url)
|
||||
|
||||
|
||||
class WM_OT_doc_view(Operator):
|
||||
|
@ -1325,10 +1355,7 @@ class WM_OT_doc_view(Operator):
|
|||
if url is None:
|
||||
return {'CANCELLED'}
|
||||
|
||||
import webbrowser
|
||||
webbrowser.open(url)
|
||||
|
||||
return {'FINISHED'}
|
||||
return bpy.ops.wm.url_open(url=url)
|
||||
|
||||
|
||||
rna_path = StringProperty(
|
||||
|
@ -1354,7 +1381,7 @@ rna_custom_property_type_items = (
|
|||
('BOOL', "Boolean", "A true or false value"),
|
||||
('BOOL_ARRAY', "Boolean Array", "An array of true or false values"),
|
||||
('STRING', "String", "A string value"),
|
||||
('PYTHON', "Python", "Edit a python value directly, for unsupported property types"),
|
||||
('PYTHON', "Python", "Edit a Python value directly, for unsupported property types"),
|
||||
)
|
||||
|
||||
rna_custom_property_subtype_none_item = ('NONE', "Plain Data", "Data values without special behavior")
|
||||
|
|
|
@ -831,7 +831,7 @@ class ConstraintButtonsPanel:
|
|||
|
||||
def draw_python_constraint(self, _context):
|
||||
layout = self.layout
|
||||
layout.label(text="Blender 2.6 doesn't support python constraints yet")
|
||||
layout.label(text="Blender 2.6 doesn't support Python constraints yet")
|
||||
|
||||
def draw_armature(self, context):
|
||||
layout = self.layout
|
||||
|
|
|
@ -168,6 +168,7 @@ class TIME_MT_cache(Menu):
|
|||
col.prop(st, "cache_softbody")
|
||||
col.prop(st, "cache_particles")
|
||||
col.prop(st, "cache_cloth")
|
||||
col.prop(st, "cache_simulation_nodes")
|
||||
col.prop(st, "cache_smoke")
|
||||
col.prop(st, "cache_dynamicpaint")
|
||||
col.prop(st, "cache_rigidbody")
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "BLI_array.hh"
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_cpp_type.hh"
|
||||
#include "BLI_generic_span.hh"
|
||||
#include "BLI_generic_virtual_array.hh"
|
||||
#include "BLI_math_color.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
|
@ -587,4 +589,16 @@ template<typename T> using DefaultMixer = typename DefaultMixerStruct<T>::type;
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generic Array Utils Implementations
|
||||
*
|
||||
* Extra implementations of functions from #BLI_array_utils.hh for all attribute types,
|
||||
* used to avoid templating the same logic for each type in many places.
|
||||
* \{ */
|
||||
|
||||
void gather(GSpan src, Span<int> map, GMutableSpan dst);
|
||||
void gather(const GVArray &src, Span<int> map, GMutableSpan dst);
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::bke::attribute_math
|
||||
|
|
|
@ -31,7 +31,7 @@ extern "C" {
|
|||
* version. Older Blender versions will test this and show a warning if the file
|
||||
* was written with too new a version. */
|
||||
#define BLENDER_FILE_MIN_VERSION 305
|
||||
#define BLENDER_FILE_MIN_SUBVERSION 9
|
||||
#define BLENDER_FILE_MIN_SUBVERSION 10
|
||||
|
||||
/** User readable version string. */
|
||||
const char *BKE_blender_version_string(void);
|
||||
|
|
|
@ -303,6 +303,12 @@ void BKE_collection_parent_relations_rebuild(struct Collection *collection);
|
|||
*/
|
||||
void BKE_main_collections_parent_relations_rebuild(struct Main *bmain);
|
||||
|
||||
/**
|
||||
* Perform some validation on integrity of the data of this collection.
|
||||
*
|
||||
* \return `true` if everything is OK, false if some errors are detected. */
|
||||
bool BKE_collection_validate(struct Collection *collection);
|
||||
|
||||
/* .blend file I/O */
|
||||
|
||||
void BKE_collection_blend_write_nolib(struct BlendWriter *writer, struct Collection *collection);
|
||||
|
|
|
@ -47,7 +47,7 @@ float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash);
|
|||
bool BKE_cryptomatte_find_name(const struct CryptomatteSession *session,
|
||||
float encoded_hash,
|
||||
char *r_name,
|
||||
int name_len);
|
||||
int name_maxncpy);
|
||||
|
||||
char *BKE_cryptomatte_entries_to_matte_id(struct NodeCryptomatte *node_storage);
|
||||
void BKE_cryptomatte_matte_id_to_entries(struct NodeCryptomatte *node_storage,
|
||||
|
|
|
@ -118,9 +118,15 @@ struct CryptomatteStampDataCallbackData {
|
|||
static blender::StringRef extract_layer_hash(blender::StringRefNull key);
|
||||
|
||||
/* C type callback function (StampCallback). */
|
||||
static void extract_layer_names(void *_data, const char *propname, char *propvalue, int len);
|
||||
static void extract_layer_names(void *_data,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int propvalue_maxncpy);
|
||||
/* C type callback function (StampCallback). */
|
||||
static void extract_layer_manifest(void *_data, const char *propname, char *propvalue, int len);
|
||||
static void extract_layer_manifest(void *_data,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int propvalue_maxncpy);
|
||||
};
|
||||
|
||||
const blender::Vector<std::string> &BKE_cryptomatte_layer_names_get(
|
||||
|
|
|
@ -50,7 +50,10 @@ void BKE_image_free_gputextures(struct Image *ima);
|
|||
*/
|
||||
void BKE_image_free_data(struct Image *image);
|
||||
|
||||
typedef void(StampCallback)(void *data, const char *propname, char *propvalue, int len);
|
||||
typedef void(StampCallback)(void *data,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int propvalue_maxncpy);
|
||||
|
||||
void BKE_render_result_stamp_info(struct Scene *scene,
|
||||
struct Object *camera,
|
||||
|
|
|
@ -422,7 +422,7 @@ bActionGroup *action_groups_add_new(bAction *act, const char name[])
|
|||
|
||||
/* make it selected, with default name */
|
||||
agrp->flag = AGRP_SELECTED;
|
||||
STRNCPY(agrp->name, name[0] ? name : DATA_("Group"));
|
||||
STRNCPY_UTF8(agrp->name, name[0] ? name : DATA_("Group"));
|
||||
|
||||
/* add to action, and validate */
|
||||
BLI_addtail(&act->groups, agrp);
|
||||
|
|
|
@ -169,7 +169,7 @@ static void action_flip_pchan(Object *ob_arm,
|
|||
BLI_str_escape(pchan_name_esc, pchan->name, sizeof(pchan_name_esc));
|
||||
const int path_xform_prefix_len = SNPRINTF(path_xform, "pose.bones[\"%s\"]", pchan_name_esc);
|
||||
char *path_xform_suffix = path_xform + path_xform_prefix_len;
|
||||
const int path_xform_suffix_len = sizeof(path_xform) - path_xform_prefix_len;
|
||||
const int path_xform_suffix_maxncpy = sizeof(path_xform) - path_xform_prefix_len;
|
||||
|
||||
/* Lookup and assign all available #FCurve channels,
|
||||
* unavailable channels are left NULL. */
|
||||
|
@ -191,11 +191,11 @@ static void action_flip_pchan(Object *ob_arm,
|
|||
} fkc_pchan = {{{NULL}}};
|
||||
|
||||
#define FCURVE_ASSIGN_VALUE(id, path_test_suffix, index) \
|
||||
BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
|
||||
BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_maxncpy); \
|
||||
action_flip_pchan_cache_fcurve_assign_value(&fkc_pchan.id, index, path_xform, fcache)
|
||||
|
||||
#define FCURVE_ASSIGN_ARRAY(id, path_test_suffix) \
|
||||
BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
|
||||
BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_maxncpy); \
|
||||
action_flip_pchan_cache_fcurve_assign_array( \
|
||||
fkc_pchan.id, ARRAY_SIZE(fkc_pchan.id), path_xform, fcache)
|
||||
|
||||
|
|
|
@ -131,8 +131,8 @@ KeyingSet *BKE_keyingset_add(
|
|||
/* allocate new KeyingSet */
|
||||
ks = MEM_callocN(sizeof(KeyingSet), "KeyingSet");
|
||||
|
||||
STRNCPY(ks->idname, (idname) ? idname : (name) ? name : DATA_("KeyingSet"));
|
||||
STRNCPY(ks->name, (name) ? name : (idname) ? idname : DATA_("Keying Set"));
|
||||
STRNCPY_UTF8(ks->idname, (idname) ? idname : (name) ? name : DATA_("KeyingSet"));
|
||||
STRNCPY_UTF8(ks->name, (name) ? name : (idname) ? idname : DATA_("Keying Set"));
|
||||
|
||||
ks->flag = flag;
|
||||
ks->keyingflag = keyingflag;
|
||||
|
|
|
@ -173,10 +173,10 @@ bool BKE_id_attribute_rename(ID *id,
|
|||
* is clamped to it's maximum length, otherwise assigning an over-long name multiple times
|
||||
* will add `.001` suffix unnecessarily. */
|
||||
{
|
||||
const int maxlength = CustomData_name_max_length_calc(new_name);
|
||||
const int new_name_maxncpy = CustomData_name_max_length_calc(new_name);
|
||||
/* NOTE: A function that performs a clamped comparison without copying would be handy here. */
|
||||
char new_name_clamped[MAX_CUSTOMDATA_LAYER_NAME];
|
||||
BLI_strncpy_utf8(new_name_clamped, new_name, maxlength);
|
||||
BLI_strncpy_utf8(new_name_clamped, new_name, new_name_maxncpy);
|
||||
if (STREQ(old_name, new_name_clamped)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -254,19 +254,14 @@ static bool unique_name_cb(void *arg, const char *name)
|
|||
bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname)
|
||||
{
|
||||
AttrUniqueData data{id};
|
||||
const int maxlength = CustomData_name_max_length_calc(name);
|
||||
const int name_maxncpy = CustomData_name_max_length_calc(name);
|
||||
|
||||
/* Set default name if none specified.
|
||||
* NOTE: We only call IFACE_() if needed to avoid locale lookup overhead. */
|
||||
if (!name || name[0] == '\0') {
|
||||
BLI_strncpy(outname, IFACE_("Attribute"), maxlength);
|
||||
}
|
||||
else {
|
||||
BLI_strncpy_utf8(outname, name, maxlength);
|
||||
}
|
||||
BLI_strncpy_utf8(outname, (name && name[0]) ? name : IFACE_("Attribute"), name_maxncpy);
|
||||
|
||||
const char *defname = ""; /* Dummy argument, never used as `name` is never zero length. */
|
||||
return BLI_uniquename_cb(unique_name_cb, &data, defname, '.', outname, maxlength);
|
||||
return BLI_uniquename_cb(unique_name_cb, &data, defname, '.', outname, name_maxncpy);
|
||||
}
|
||||
|
||||
CustomDataLayer *BKE_id_attribute_new(ID *id,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
|
||||
namespace blender::bke::attribute_math {
|
||||
|
@ -128,4 +130,20 @@ void ColorGeometry4bMixer::finalize(const IndexMask mask)
|
|||
});
|
||||
}
|
||||
|
||||
void gather(const GSpan src, const Span<int> map, GMutableSpan dst)
|
||||
{
|
||||
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
array_utils::gather(src.typed<T>(), map, dst.typed<T>());
|
||||
});
|
||||
}
|
||||
|
||||
void gather(const GVArray &src, const Span<int> map, GMutableSpan dst)
|
||||
{
|
||||
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
array_utils::gather(src.typed<T>(), map, dst.typed<T>());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender::bke::attribute_math
|
||||
|
|
|
@ -1617,7 +1617,7 @@ BoidRule *boid_new_rule(int type)
|
|||
|
||||
rule->type = type;
|
||||
rule->flag |= BOIDRULE_IN_AIR | BOIDRULE_ON_LAND;
|
||||
STRNCPY(rule->name, DATA_(rna_enum_boidrule_type_items[type - 1].name));
|
||||
STRNCPY_UTF8(rule->name, DATA_(rna_enum_boidrule_type_items[type - 1].name));
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
|
|
@ -1944,6 +1944,49 @@ void BKE_main_collections_parent_relations_rebuild(Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
bool BKE_collection_validate(struct Collection *collection)
|
||||
{
|
||||
if (!BLI_listbase_validate(&collection->children)) {
|
||||
return false;
|
||||
}
|
||||
if (!BLI_listbase_validate(&collection->runtime.parents)) {
|
||||
return false;
|
||||
}
|
||||
if (BKE_collection_cycle_find(collection, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_ok = true;
|
||||
|
||||
/* Check that children have each collection used/referenced only once. */
|
||||
GSet *processed_collections = BLI_gset_ptr_new(__func__);
|
||||
for (CollectionChild *child = collection->children.first; child; child = child->next) {
|
||||
void **r_key;
|
||||
if (BLI_gset_ensure_p_ex(processed_collections, child->collection, &r_key)) {
|
||||
is_ok = false;
|
||||
}
|
||||
else {
|
||||
*r_key = child->collection;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that parents have each collection used/referenced only once. */
|
||||
BLI_gset_clear(processed_collections, NULL);
|
||||
for (CollectionParent *parent = collection->runtime.parents.first; parent; parent = parent->next)
|
||||
{
|
||||
void **r_key;
|
||||
if (BLI_gset_ensure_p_ex(processed_collections, parent->collection, &r_key)) {
|
||||
is_ok = false;
|
||||
}
|
||||
else {
|
||||
*r_key = parent->collection;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_gset_free(processed_collections, NULL);
|
||||
return is_ok;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -219,14 +219,14 @@ float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash)
|
|||
bool BKE_cryptomatte_find_name(const CryptomatteSession *session,
|
||||
const float encoded_hash,
|
||||
char *r_name,
|
||||
int name_len)
|
||||
int name_maxncpy)
|
||||
{
|
||||
std::optional<std::string> name = (*session)[encoded_hash];
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BLI_strncpy(r_name, name->c_str(), name_len);
|
||||
BLI_strncpy(r_name, name->c_str(), name_maxncpy);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -580,7 +580,7 @@ blender::StringRef CryptomatteStampDataCallbackData::extract_layer_hash(blender:
|
|||
void CryptomatteStampDataCallbackData::extract_layer_names(void *_data,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int /*len*/)
|
||||
int /*propvalue_maxncpy*/)
|
||||
{
|
||||
CryptomatteStampDataCallbackData *data = static_cast<CryptomatteStampDataCallbackData *>(_data);
|
||||
|
||||
|
@ -598,7 +598,7 @@ void CryptomatteStampDataCallbackData::extract_layer_names(void *_data,
|
|||
void CryptomatteStampDataCallbackData::extract_layer_manifest(void *_data,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int /*len*/)
|
||||
int /*propvalue_maxncpy*/)
|
||||
{
|
||||
CryptomatteStampDataCallbackData *data = static_cast<CryptomatteStampDataCallbackData *>(_data);
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ TEST(cryptomatte, extract_layer_hash_from_metadata_key)
|
|||
static void validate_cryptomatte_session_from_stamp_data(void * /*data*/,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int /*len*/)
|
||||
int /*propvalue_maxncpy*/)
|
||||
{
|
||||
blender::StringRefNull prop_name(propname);
|
||||
if (!prop_name.startswith("cryptomatte/")) {
|
||||
|
|
|
@ -1124,14 +1124,6 @@ static void copy_construct_data(const GSpan src, GMutableSpan dst)
|
|||
src.type().copy_construct_n(src.data(), dst.data(), src.size());
|
||||
}
|
||||
|
||||
static void copy_with_map(const GSpan src, const Span<int> map, GMutableSpan dst)
|
||||
{
|
||||
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
array_utils::gather(src.typed<T>(), map, dst.typed<T>());
|
||||
});
|
||||
}
|
||||
|
||||
static CurvesGeometry copy_with_removed_points(
|
||||
const CurvesGeometry &curves,
|
||||
const IndexMask points_to_delete,
|
||||
|
@ -1216,7 +1208,7 @@ static CurvesGeometry copy_with_removed_points(
|
|||
attribute.dst.span.copy_from(attribute.src);
|
||||
}
|
||||
else {
|
||||
copy_with_map(attribute.src, new_curve_orig_indices, attribute.dst.span);
|
||||
bke::attribute_math::gather(attribute.src, new_curve_orig_indices, attribute.dst.span);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "BLI_span.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
|
@ -4413,7 +4414,7 @@ void CustomData_set_layer_unique_name(CustomData *data, const int index)
|
|||
/* Set default name if none specified. Note we only call DATA_() when
|
||||
* needed to avoid overhead of locale lookups in the depsgraph. */
|
||||
if (nlayer->name[0] == '\0') {
|
||||
STRNCPY(nlayer->name, DATA_(typeInfo->defaultname));
|
||||
STRNCPY_UTF8(nlayer->name, DATA_(typeInfo->defaultname));
|
||||
}
|
||||
|
||||
const char *defname = ""; /* Dummy argument, never used as `name` is never zero length. */
|
||||
|
@ -4437,10 +4438,10 @@ void CustomData_validate_layer_name(const CustomData *data,
|
|||
* deleted, so assign the active layer to name
|
||||
*/
|
||||
index = CustomData_get_active_layer_index(data, type);
|
||||
BLI_strncpy(outname, data->layers[index].name, MAX_CUSTOMDATA_LAYER_NAME);
|
||||
BLI_strncpy_utf8(outname, data->layers[index].name, MAX_CUSTOMDATA_LAYER_NAME);
|
||||
}
|
||||
else {
|
||||
BLI_strncpy(outname, name, MAX_CUSTOMDATA_LAYER_NAME);
|
||||
BLI_strncpy_utf8(outname, name, MAX_CUSTOMDATA_LAYER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -405,7 +405,7 @@ static bool surface_duplicateNameExists(void *arg, const char *name)
|
|||
void dynamicPaintSurface_setUniqueName(DynamicPaintSurface *surface, const char *basename)
|
||||
{
|
||||
char name[64];
|
||||
STRNCPY(name, basename); /* in case basename is surface->name use a copy */
|
||||
STRNCPY_UTF8(name, basename); /* in case basename is surface->name use a copy */
|
||||
BLI_uniquename_cb(
|
||||
surface_duplicateNameExists, surface, name, '.', surface->name, sizeof(surface->name));
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "BLI_expr_pylike_eval.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
@ -955,7 +956,7 @@ DriverVar *driver_add_new_variable(ChannelDriver *driver)
|
|||
BLI_addtail(&driver->variables, dvar);
|
||||
|
||||
/* Give the variable a 'unique' name. */
|
||||
strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"));
|
||||
STRNCPY_UTF8(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"));
|
||||
BLI_uniquename(&driver->variables,
|
||||
dvar,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"),
|
||||
|
|
|
@ -675,7 +675,7 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd,
|
|||
}
|
||||
|
||||
/* auto-name */
|
||||
STRNCPY(gpl->info, DATA_(name));
|
||||
STRNCPY_UTF8(gpl->info, DATA_(name));
|
||||
BLI_uniquename(&gpd->layers,
|
||||
gpl,
|
||||
(gpd->flag & GP_DATA_ANNOTATIONS) ? DATA_("Note") : DATA_("GP_Layer"),
|
||||
|
|
|
@ -356,7 +356,7 @@ GpencilModifierData *BKE_gpencil_modifier_new(int type)
|
|||
GpencilModifierData *md = MEM_callocN(mti->struct_size, mti->struct_name);
|
||||
|
||||
/* NOTE: this name must be made unique later. */
|
||||
STRNCPY(md->name, DATA_(mti->name));
|
||||
STRNCPY_UTF8(md->name, DATA_(mti->name));
|
||||
|
||||
md->type = type;
|
||||
md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render;
|
||||
|
|
|
@ -2486,18 +2486,24 @@ void BKE_stamp_data_free(StampData *stamp_data)
|
|||
}
|
||||
|
||||
/* wrap for callback only */
|
||||
static void metadata_set_field(void *data, const char *propname, char *propvalue, int /*len*/)
|
||||
static void metadata_set_field(void *data,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int /*propvalue_maxncpy*/)
|
||||
{
|
||||
/* We know it is an ImBuf* because that's what we pass to BKE_stamp_info_callback. */
|
||||
ImBuf *imbuf = static_cast<ImBuf *>(data);
|
||||
IMB_metadata_set_field(imbuf->metadata, propname, propvalue);
|
||||
}
|
||||
|
||||
static void metadata_get_field(void *data, const char *propname, char *propvalue, int len)
|
||||
static void metadata_get_field(void *data,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int propvalue_maxncpy)
|
||||
{
|
||||
/* We know it is an ImBuf* because that's what we pass to BKE_stamp_info_callback. */
|
||||
ImBuf *imbuf = static_cast<ImBuf *>(data);
|
||||
IMB_metadata_get_field(imbuf->metadata, propname, propvalue, len);
|
||||
IMB_metadata_get_field(imbuf->metadata, propname, propvalue, propvalue_maxncpy);
|
||||
}
|
||||
|
||||
void BKE_imbuf_stamp_info(const RenderResult *rr, ImBuf *ibuf)
|
||||
|
|
|
@ -1845,7 +1845,7 @@ KeyBlock *BKE_keyblock_add(Key *key, const char *name)
|
|||
}
|
||||
else {
|
||||
if (tot == 1) {
|
||||
STRNCPY(kb->name, DATA_("Basis"));
|
||||
STRNCPY_UTF8(kb->name, DATA_("Basis"));
|
||||
}
|
||||
else {
|
||||
SNPRINTF(kb->name, DATA_("Key %d"), tot - 1);
|
||||
|
|
|
@ -787,7 +787,7 @@ void BKE_layer_collection_resync_allow(void)
|
|||
}
|
||||
|
||||
struct LayerCollectionResync {
|
||||
LayerCollectionResync *prev, *next;
|
||||
LayerCollectionResync *next, *prev;
|
||||
|
||||
/* Temp data used to generate a queue during valid layer search. See
|
||||
* #layer_collection_resync_find. */
|
||||
|
@ -1006,7 +1006,7 @@ void BKE_main_view_layers_synced_ensure(const Main *bmain)
|
|||
BKE_scene_view_layers_synced_ensure(scene);
|
||||
}
|
||||
|
||||
/* NOTE: This is not (yet?) covered by the dirty tag and differed re-sync system */
|
||||
/* NOTE: This is not (yet?) covered by the dirty tag and deffered re-sync system. */
|
||||
BKE_layer_collection_local_sync_all(bmain);
|
||||
}
|
||||
|
||||
|
@ -2522,7 +2522,7 @@ ViewLayerAOV *BKE_view_layer_add_aov(ViewLayer *view_layer)
|
|||
ViewLayerAOV *aov;
|
||||
aov = MEM_cnew<ViewLayerAOV>(__func__);
|
||||
aov->type = AOV_TYPE_COLOR;
|
||||
STRNCPY(aov->name, DATA_("AOV"));
|
||||
STRNCPY_UTF8(aov->name, DATA_("AOV"));
|
||||
BLI_addtail(&view_layer->aovs, aov);
|
||||
viewlayer_aov_active_set(view_layer, aov);
|
||||
viewlayer_aov_make_name_unique(view_layer);
|
||||
|
@ -2642,12 +2642,7 @@ ViewLayerLightgroup *BKE_view_layer_add_lightgroup(ViewLayer *view_layer, const
|
|||
{
|
||||
ViewLayerLightgroup *lightgroup;
|
||||
lightgroup = MEM_cnew<ViewLayerLightgroup>(__func__);
|
||||
if (name && name[0]) {
|
||||
STRNCPY(lightgroup->name, name);
|
||||
}
|
||||
else {
|
||||
STRNCPY(lightgroup->name, DATA_("Lightgroup"));
|
||||
}
|
||||
STRNCPY_UTF8(lightgroup->name, (name && name[0]) ? name : DATA_("Lightgroup"));
|
||||
BLI_addtail(&view_layer->lightgroups, lightgroup);
|
||||
viewlayer_lightgroup_active_set(view_layer, lightgroup);
|
||||
viewlayer_lightgroup_make_name_unique(view_layer, lightgroup);
|
||||
|
|
|
@ -1603,7 +1603,7 @@ bool BKE_id_new_name_validate(
|
|||
|
||||
if (name[0] == '\0') {
|
||||
/* Disallow empty names. */
|
||||
STRNCPY(name, DATA_(BKE_idtype_idcode_to_name(GS(id->name))));
|
||||
STRNCPY_UTF8(name, DATA_(BKE_idtype_idcode_to_name(GS(id->name))));
|
||||
}
|
||||
else {
|
||||
/* disallow non utf8 chars,
|
||||
|
@ -1666,6 +1666,10 @@ void BKE_main_id_refcount_recompute(struct Main *bmain, const bool do_linked_onl
|
|||
id->tag &= ~(LIB_TAG_EXTRAUSER | LIB_TAG_EXTRAUSER_SET);
|
||||
id_us_ensure_real(id);
|
||||
}
|
||||
if (ELEM(GS(id->name), ID_SCE, ID_WM, ID_WS)) {
|
||||
/* These IDs should always have a 'virtual' user. */
|
||||
id_us_ensure_real(id);
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
|
|
|
@ -404,6 +404,11 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner, const bool include
|
|||
return FILTER_ID_ALL;
|
||||
}
|
||||
|
||||
if (ID_IS_OVERRIDE_LIBRARY_REAL(id_owner)) {
|
||||
/* LibOverride data 'hierarchy root' can virtually point back to any type of ID. */
|
||||
return FILTER_ID_ALL;
|
||||
}
|
||||
|
||||
switch ((ID_Type)id_type_owner) {
|
||||
case ID_LI:
|
||||
return FILTER_ID_LI;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "BLI_path_util.h"
|
||||
#include "BLI_session_uuid.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
|
@ -137,7 +138,7 @@ static ModifierData *modifier_allocate_and_init(ModifierType type)
|
|||
ModifierData *md = static_cast<ModifierData *>(MEM_callocN(mti->structSize, mti->structName));
|
||||
|
||||
/* NOTE: this name must be made unique later. */
|
||||
STRNCPY(md->name, DATA_(mti->name));
|
||||
STRNCPY_UTF8(md->name, DATA_(mti->name));
|
||||
|
||||
md->type = type;
|
||||
md->mode = eModifierMode_Realtime | eModifierMode_Render;
|
||||
|
|
|
@ -1521,8 +1521,10 @@ void multires_ensure_external_read(struct Mesh *mesh, int top_level)
|
|||
return;
|
||||
}
|
||||
|
||||
MDisps *mdisps = static_cast<MDisps *>(
|
||||
CustomData_get_layer_for_write(&mesh->ldata, CD_MDISPS, mesh->totloop));
|
||||
/* Modify the data array from the original mesh, not the evaluated mesh.
|
||||
* When multiple objects share the same mesh, this can lead to memory leaks. */
|
||||
MDisps *mdisps = const_cast<MDisps *>(
|
||||
static_cast<const MDisps *>(CustomData_get_layer(&mesh->ldata, CD_MDISPS)));
|
||||
if (mdisps == nullptr) {
|
||||
mdisps = static_cast<MDisps *>(
|
||||
CustomData_add_layer(&mesh->ldata, CD_MDISPS, CD_SET_DEFAULT, mesh->totloop));
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "BLI_set.hh"
|
||||
#include "BLI_stack.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
@ -1159,7 +1160,7 @@ static void node_init(const bContext *C, bNodeTree *ntree, bNode *node)
|
|||
* Data have their own translation option!
|
||||
* This solution may be a bit rougher than nodeLabel()'s returned string, but it's simpler
|
||||
* than adding "do_translate" flags to this func (and labelfunc() as well). */
|
||||
STRNCPY(node->name, DATA_(ntype->ui_name));
|
||||
STRNCPY_UTF8(node->name, DATA_(ntype->ui_name));
|
||||
nodeUniqueName(ntree, node);
|
||||
|
||||
/* Generally sockets should be added after the initialization, because the set of sockets might
|
||||
|
|
|
@ -949,12 +949,12 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id)
|
|||
if (ob->id.lib) {
|
||||
BLO_reportf_wrap(reports,
|
||||
RPT_INFO,
|
||||
TIP_("Can't find object data of %s lib %s\n"),
|
||||
TIP_("Can't find object data of %s lib %s"),
|
||||
ob->id.name + 2,
|
||||
ob->id.lib->filepath);
|
||||
}
|
||||
else {
|
||||
BLO_reportf_wrap(reports, RPT_INFO, TIP_("Object %s lost data\n"), ob->id.name + 2);
|
||||
BLO_reportf_wrap(reports, RPT_INFO, TIP_("Object %s lost data"), ob->id.name + 2);
|
||||
}
|
||||
reports->count.missing_obdata++;
|
||||
}
|
||||
|
|
|
@ -481,9 +481,9 @@ char *BKE_packedfile_unpack_to_file(ReportList *reports,
|
|||
static void unpack_generate_paths(const char *filepath,
|
||||
ID *id,
|
||||
char *r_abspath,
|
||||
size_t abspath_maxncpy,
|
||||
char *r_relpath,
|
||||
size_t abspathlen,
|
||||
size_t relpathlen)
|
||||
size_t relpath_maxncpy)
|
||||
{
|
||||
const short id_type = GS(id->name);
|
||||
char temp_filename[FILE_MAX];
|
||||
|
@ -545,13 +545,13 @@ static void unpack_generate_paths(const char *filepath,
|
|||
break;
|
||||
}
|
||||
if (dir_name) {
|
||||
BLI_path_join(r_relpath, relpathlen, "//", dir_name, temp_filename);
|
||||
BLI_path_join(r_relpath, relpath_maxncpy, "//", dir_name, temp_filename);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
size_t len = BLI_strncpy_rlen(r_abspath, temp_dirname, abspathlen);
|
||||
BLI_strncpy(r_abspath + len, temp_filename, abspathlen - len);
|
||||
size_t len = BLI_strncpy_rlen(r_abspath, temp_dirname, abspath_maxncpy);
|
||||
BLI_strncpy(r_abspath + len, temp_filename, abspath_maxncpy - len);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,7 +567,7 @@ char *BKE_packedfile_unpack(Main *bmain,
|
|||
|
||||
if (id != NULL) {
|
||||
unpack_generate_paths(
|
||||
orig_file_path, id, absname, localname, sizeof(absname), sizeof(localname));
|
||||
orig_file_path, id, absname, sizeof(absname), localname, sizeof(localname));
|
||||
new_name = BKE_packedfile_unpack_to_file(
|
||||
reports, BKE_main_blendfile_path(bmain), absname, localname, pf, how);
|
||||
}
|
||||
|
|
|
@ -910,7 +910,7 @@ static bool seq_foreach_path_callback(Sequence *seq, void *user_data)
|
|||
BPathForeachPathData *bpath_data = (BPathForeachPathData *)user_data;
|
||||
|
||||
if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_SOUND_RAM) && se) {
|
||||
BKE_bpath_foreach_path_dirfile_fixed_process(bpath_data, seq->strip->dir, se->name);
|
||||
BKE_bpath_foreach_path_dirfile_fixed_process(bpath_data, seq->strip->dirpath, se->filename);
|
||||
}
|
||||
else if ((seq->type == SEQ_TYPE_IMAGE) && se) {
|
||||
/* NOTE: An option not to loop over all strips could be useful? */
|
||||
|
@ -923,12 +923,13 @@ static bool seq_foreach_path_callback(Sequence *seq, void *user_data)
|
|||
}
|
||||
|
||||
for (i = 0; i < len; i++, se++) {
|
||||
BKE_bpath_foreach_path_dirfile_fixed_process(bpath_data, seq->strip->dir, se->name);
|
||||
BKE_bpath_foreach_path_dirfile_fixed_process(
|
||||
bpath_data, seq->strip->dirpath, se->filename);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* simple case */
|
||||
BKE_bpath_foreach_path_fixed_process(bpath_data, seq->strip->dir);
|
||||
BKE_bpath_foreach_path_fixed_process(bpath_data, seq->strip->dirpath);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -65,7 +65,7 @@ ShaderFxData *BKE_shaderfx_new(int type)
|
|||
ShaderFxData *fx = MEM_callocN(fxi->struct_size, fxi->struct_name);
|
||||
|
||||
/* NOTE: this name must be made unique later. */
|
||||
STRNCPY(fx->name, DATA_(fxi->name));
|
||||
STRNCPY_UTF8(fx->name, DATA_(fxi->name));
|
||||
|
||||
fx->type = type;
|
||||
fx->mode = eShaderFxMode_Realtime | eShaderFxMode_Render;
|
||||
|
|
|
@ -68,7 +68,7 @@ void ModifierSimulationCache::try_discover_bake(const StringRefNull meta_dir,
|
|||
if (!dir_entry_path.endswith(".json")) {
|
||||
continue;
|
||||
}
|
||||
char modified_file_name[FILENAME_MAX];
|
||||
char modified_file_name[FILE_MAX];
|
||||
STRNCPY(modified_file_name, dir_entry.relname);
|
||||
BLI_str_replace_char(modified_file_name, '_', '.');
|
||||
|
||||
|
|
|
@ -164,13 +164,13 @@ static void filepath_avi(char *filepath, const RenderData *rd, bool preview, con
|
|||
|
||||
if (rd->scemode & R_EXTENSION) {
|
||||
if (!BLI_path_extension_check(filepath, ".avi")) {
|
||||
BLI_path_frame_range(filepath, sfra, efra, 4);
|
||||
BLI_path_frame_range(filepath, FILE_MAX, sfra, efra, 4);
|
||||
BLI_strncat(filepath, ".avi", FILE_MAX);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (BLI_path_frame_check_chars(filepath)) {
|
||||
BLI_path_frame_range(filepath, sfra, efra, 4);
|
||||
BLI_path_frame_range(filepath, FILE_MAX, sfra, efra, 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1081,7 +1081,7 @@ static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value)
|
|||
static void ffmpeg_add_metadata_callback(void *data,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int UNUSED(len))
|
||||
int UNUSED(propvalue_maxncpy))
|
||||
{
|
||||
AVDictionary **metadata = (AVDictionary **)data;
|
||||
av_dict_set(metadata, propname, propvalue, 0);
|
||||
|
@ -1406,7 +1406,7 @@ static void ffmpeg_filepath_get(FFMpegContext *context,
|
|||
if (*fe == NULL) {
|
||||
BLI_strncat(string, autosplit, FILE_MAX);
|
||||
|
||||
BLI_path_frame_range(string, sfra, efra, 4);
|
||||
BLI_path_frame_range(string, FILE_MAX, sfra, efra, 4);
|
||||
BLI_strncat(string, *exts, FILE_MAX);
|
||||
}
|
||||
else {
|
||||
|
@ -1417,7 +1417,7 @@ static void ffmpeg_filepath_get(FFMpegContext *context,
|
|||
}
|
||||
else {
|
||||
if (BLI_path_frame_check_chars(string)) {
|
||||
BLI_path_frame_range(string, sfra, efra, 4);
|
||||
BLI_path_frame_range(string, FILE_MAX, sfra, efra, 4);
|
||||
}
|
||||
|
||||
BLI_strncat(string, autosplit, FILE_MAX);
|
||||
|
|
|
@ -285,6 +285,10 @@ BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
|
|||
lb->first = lb->last = (void *)0;
|
||||
}
|
||||
|
||||
/** Validate the integrity of a given ListBase, returns `true` if evrything is OK, false otherwise.
|
||||
*/
|
||||
bool BLI_listbase_validate(struct ListBase *lb);
|
||||
|
||||
/**
|
||||
* Equality check for ListBase.
|
||||
*
|
||||
|
|
|
@ -15,27 +15,219 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Queries
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Sets the specified environment variable to the specified value,
|
||||
* and clears it if `val == NULL`.
|
||||
*/
|
||||
void BLI_setenv(const char *env, const char *val) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Only set an environment variable if already not there.
|
||||
* Like Unix `setenv(env, val, 0);`
|
||||
* Get an element of the path at an index, eg:
|
||||
* `/some/path/file.txt` where an index of:
|
||||
* - 0 or -3: `some`
|
||||
* - 1 or -2: `path`
|
||||
* - 2 or -1: `file.txt`
|
||||
*
|
||||
* (not used anywhere).
|
||||
* Ignored elements in the path:
|
||||
* - Multiple slashes at any point in the path (including start/end).
|
||||
* - Single '.' in the path: `/./` except for the beginning of the path
|
||||
* where it's used to signify a $PWD relative path.
|
||||
*/
|
||||
void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1);
|
||||
bool BLI_path_name_at_index(const char *__restrict path,
|
||||
int index,
|
||||
int *__restrict r_offset,
|
||||
int *__restrict r_len) ATTR_NONNULL(1, 3, 4) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Get an environment variable, result has to be used immediately.
|
||||
*
|
||||
* On windows #getenv gets its variables from a static copy of the environment variables taken at
|
||||
* process start-up, causing it to not pick up on environment variables created during runtime.
|
||||
* This function uses an alternative method to get environment variables that does pick up on
|
||||
* runtime environment variables. The result will be UTF-8 encoded.
|
||||
* Return true if the path is a UNC share.
|
||||
*/
|
||||
const char *BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
bool BLI_path_is_unc(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Parent Operations
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Go back one directory.
|
||||
*
|
||||
* Replaces path with the path of its parent directory, returning true if
|
||||
* it was able to find a parent directory within the path.
|
||||
*
|
||||
* On success, the resulting path will always have a trailing slash.
|
||||
*/
|
||||
bool BLI_path_parent_dir(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Go back until the directory is found.
|
||||
*
|
||||
* Strips off nonexistent (or non-accessible) sub-directories from the end of `dir`,
|
||||
* leaving the path of the lowest-level directory that does exist and we can read.
|
||||
*/
|
||||
bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* In the simple case this is similar to `BLI_path_slash_rfind(dirname)`
|
||||
* however it behaves differently when there are redundant characters:
|
||||
*
|
||||
* `/test///dir/./file`
|
||||
* ^
|
||||
* `/test/dir/subdir//file`
|
||||
* ^
|
||||
* \return The position after the parent paths last character or NULL on failure.
|
||||
* Neither `path` or `&path[path_len - 1]` are ever returned.
|
||||
*/
|
||||
const char *BLI_path_parent_dir_end(const char *path, size_t path_len)
|
||||
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Make Safe / Sanitize
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Make given name safe to be used in paths.
|
||||
*
|
||||
* \param allow_tokens: Permit the usage of '<' and '>' characters. This can be
|
||||
* leveraged by higher layers to support "virtual filenames" which contain
|
||||
* substitution markers delineated between the two characters.
|
||||
*
|
||||
* \return true if \a fname was changed, false otherwise.
|
||||
*
|
||||
* For now, simply replaces reserved chars (as listed in
|
||||
* https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words )
|
||||
* by underscores ('_').
|
||||
*
|
||||
* \note Space case ' ' is a bit of an edge case here - in theory it is allowed,
|
||||
* but again can be an issue in some cases, so we simply replace it by an underscore too
|
||||
* (good practice anyway).
|
||||
* REMOVED based on popular demand (see #45900).
|
||||
* Percent '%' char is a bit same case - not recommended to use it,
|
||||
* but supported by all decent file-systems/operating-systems around.
|
||||
*
|
||||
* \note On Windows, it also ensures there is no '.' (dot char) at the end of the file,
|
||||
* this can lead to issues.
|
||||
*
|
||||
* \note On Windows, it also checks for forbidden names
|
||||
* (see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx ).
|
||||
*/
|
||||
bool BLI_path_make_safe_filename_ex(char *fname, bool allow_tokens) ATTR_NONNULL(1);
|
||||
bool BLI_path_make_safe_filename(char *fname) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Make given path OS-safe.
|
||||
*
|
||||
* \return true if \a path was changed, false otherwise.
|
||||
*/
|
||||
bool BLI_path_make_safe(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Creates a display string from path to be used menus and the user interface.
|
||||
* Like `bpy.path.display_name()`.
|
||||
*/
|
||||
void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name)
|
||||
ATTR_NONNULL(1, 3);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Normalize
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Remove redundant characters from \a path.
|
||||
*
|
||||
* The following operations are performed:
|
||||
* - Redundant path components such as `//`, `/./` & `./` (prefix) are stripped.
|
||||
* (with the exception of `//` prefix used for blend-file relative paths).
|
||||
* - `..` are resolved so `<parent>/../<child>/` resolves to `<child>/`.
|
||||
* Note that the resulting path may begin with `..` if it's relative.
|
||||
*
|
||||
* Details:
|
||||
* - The slash direction is expected to be native (see #SEP).
|
||||
* When calculating a canonical paths you may need to run #BLI_path_slash_native first.
|
||||
* #BLI_path_cmp_normalized can be used for canonical path comparison.
|
||||
* - Trailing slashes are left intact (unlike Python which strips them).
|
||||
* - Handling paths beginning with `..` depends on them being absolute or relative.
|
||||
* For absolute paths they are removed (e.g. `/../path` becomes `/path`).
|
||||
* For relative paths they are kept as it's valid to reference paths above a relative location
|
||||
* such as `//../parent` or `../parent`.
|
||||
*
|
||||
* \param path: The path to a file or directory which can be absolute or relative.
|
||||
*/
|
||||
void BLI_path_normalize(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Cleanup file-path ensuring a trailing slash.
|
||||
*
|
||||
* \note Same as #BLI_path_normalize but adds a trailing slash.
|
||||
*/
|
||||
void BLI_path_normalize_dir(char *dir, size_t dir_maxncpy) ATTR_NONNULL(1);
|
||||
|
||||
#if defined(WIN32)
|
||||
void BLI_path_normalize_unc_16(wchar_t *path_16);
|
||||
void BLI_path_normalize_unc(char *path, int path_maxncpy);
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path FileName Manipulation
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Ensure `filepath` has a file component, adding `filename` when it's empty or ends with a slash.
|
||||
* \return true if the `filename` was appended to `filepath`.
|
||||
*/
|
||||
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename)
|
||||
ATTR_NONNULL(1, 3);
|
||||
|
||||
/**
|
||||
* Appends a suffix to the `path`, fitting it before the extension
|
||||
*
|
||||
* path = `Foo.png`, suffix = `123`, separator = `_`.
|
||||
* `Foo.png` -> `Foo_123.png`.
|
||||
*
|
||||
* \param path: original (and final) string.
|
||||
* \param path_maxncpy: Maximum length of path.
|
||||
* \param suffix: String to append to the original path.
|
||||
* \param sep: Optional separator character.
|
||||
* \return true if succeeded.
|
||||
*/
|
||||
bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const char *sep)
|
||||
ATTR_NONNULL(1, 3, 4);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Slash Utilities
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \return pointer to the leftmost path separator in path (or NULL when not found).
|
||||
*/
|
||||
const char *BLI_path_slash_find(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* \return pointer to the rightmost path separator in path (or NULL when not found).
|
||||
*/
|
||||
const char *BLI_path_slash_rfind(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Appends a slash to path if there isn't one there already.
|
||||
* Returns the new length of the path.
|
||||
*/
|
||||
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Removes the last slash and everything after it to the end of path, if there is one.
|
||||
*/
|
||||
void BLI_path_slash_rstrip(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Changes to the path separators to the native ones for this OS.
|
||||
*/
|
||||
void BLI_path_slash_native(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Directory/FileName Split
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Copies directory and file components from `filepath` into `dir` and `file`, e.g.
|
||||
|
@ -58,17 +250,20 @@ void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy
|
|||
*/
|
||||
void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy)
|
||||
ATTR_NONNULL(1, 2);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the last extension (e.g. the position of the last period).
|
||||
* Returns a pointer to the nil byte when no extension is found.
|
||||
* Like Python's `os.path.basename()`
|
||||
*
|
||||
* \return The pointer into \a path string immediately after last slash,
|
||||
* or start of \a path if none found.
|
||||
*/
|
||||
const char *BLI_path_extension_or_end(const char *filepath)
|
||||
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
|
||||
/**
|
||||
* Returns a pointer to the last extension (e.g. the position of the last period).
|
||||
* Returns NULL if there is no extension.
|
||||
*/
|
||||
const char *BLI_path_extension(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
const char *BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Append
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Append a filename to a dir, ensuring slash separates.
|
||||
|
@ -83,6 +278,12 @@ size_t BLI_path_append(char *__restrict dst, size_t dst_maxncpy, const char *__r
|
|||
size_t BLI_path_append_dir(char *__restrict dst, size_t dst_maxncpy, const char *__restrict dir)
|
||||
ATTR_NONNULL(1, 3);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Join
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* See #BLI_path_join doc-string.
|
||||
*/
|
||||
|
@ -192,65 +393,23 @@ BLI_INLINE size_t _BLI_path_join_12(_BLI_PATH_JOIN_ARGS_10)
|
|||
#undef _BLI_PATH_JOIN_ARGS_9
|
||||
#undef _BLI_PATH_JOIN_ARGS_10
|
||||
|
||||
/**
|
||||
* Like Python's `os.path.basename()`
|
||||
*
|
||||
* \return The pointer into \a path string immediately after last slash,
|
||||
* or start of \a path if none found.
|
||||
*/
|
||||
const char *BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Get an element of the path at an index, eg:
|
||||
* `/some/path/file.txt` where an index of:
|
||||
* - 0 or -3: `some`
|
||||
* - 1 or -2: `path`
|
||||
* - 2 or -1: `file.txt`
|
||||
*
|
||||
* Ignored elements in the path:
|
||||
* - Multiple slashes at any point in the path (including start/end).
|
||||
* - Single '.' in the path: `/./` except for the beginning of the path
|
||||
* where it's used to signify a $PWD relative path.
|
||||
*/
|
||||
bool BLI_path_name_at_index(const char *__restrict path,
|
||||
int index,
|
||||
int *__restrict r_offset,
|
||||
int *__restrict r_len) ATTR_NONNULL(1, 3, 4) ATTR_WARN_UNUSED_RESULT;
|
||||
/** \} */
|
||||
|
||||
/** Return true only if #containee_path is contained in #container_path. */
|
||||
bool BLI_path_contains(const char *container_path, const char *containee_path)
|
||||
ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT;
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path File Extensions
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \return pointer to the leftmost path separator in path (or NULL when not found).
|
||||
* Returns a pointer to the last extension (e.g. the position of the last period).
|
||||
* Returns a pointer to the nil byte when no extension is found.
|
||||
*/
|
||||
const char *BLI_path_slash_find(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
const char *BLI_path_extension_or_end(const char *filepath)
|
||||
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
|
||||
/**
|
||||
* \return pointer to the rightmost path separator in path (or NULL when not found).
|
||||
* Returns a pointer to the last extension (e.g. the position of the last period).
|
||||
* Returns NULL if there is no extension.
|
||||
*/
|
||||
const char *BLI_path_slash_rfind(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Appends a slash to path if there isn't one there already.
|
||||
* Returns the new length of the path.
|
||||
*/
|
||||
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Removes the last slash and everything after it to the end of path, if there is one.
|
||||
*/
|
||||
void BLI_path_slash_rstrip(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Changes to the path separators to the native ones for this OS.
|
||||
*/
|
||||
void BLI_path_slash_native(char *path) ATTR_NONNULL(1);
|
||||
|
||||
#ifdef _WIN32
|
||||
bool BLI_path_program_extensions_add_win32(char *program_name, size_t program_name_maxncpy);
|
||||
#endif
|
||||
/**
|
||||
* Search for a binary (executable)
|
||||
*/
|
||||
bool BLI_path_program_search(char *program_filepath,
|
||||
size_t program_filepath_maxncpy,
|
||||
const char *program_name) ATTR_NONNULL(1, 3);
|
||||
const char *BLI_path_extension(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* \return true when `path` end with `ext` (case insensitive).
|
||||
|
@ -295,230 +454,12 @@ bool BLI_path_extension_strip(char *path) ATTR_NONNULL(1);
|
|||
*/
|
||||
bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext)
|
||||
ATTR_NONNULL(1, 3);
|
||||
/**
|
||||
* Ensure `filepath` has a file component, adding `filename` when it's empty or ends with a slash.
|
||||
* \return true if the `filename` was appended to `filepath`.
|
||||
*/
|
||||
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename)
|
||||
ATTR_NONNULL(1, 3);
|
||||
/**
|
||||
* Looks for a sequence of decimal digits in `path`, preceding any filename extension,
|
||||
* returning the integer value if found, or 0 if not.
|
||||
*
|
||||
* \param path: String to scan.
|
||||
* \param head: Optional area to return copy of part of `path` prior to digits,
|
||||
* or before dot if no digits.
|
||||
* \param tail: Optional area to return copy of part of `path` following digits,
|
||||
* or from dot if no digits.
|
||||
* \param r_digits_len: Optional to return number of digits found.
|
||||
*/
|
||||
int BLI_path_sequence_decode(const char *path,
|
||||
char *head,
|
||||
size_t head_maxncpy,
|
||||
char *tail,
|
||||
size_t tail_maxncpy,
|
||||
unsigned short *r_digits_len);
|
||||
/**
|
||||
* Returns in area pointed to by `path` a string of the form `<head><pic><tail>`,
|
||||
* where pic is formatted as `numlen` digits with leading zeroes.
|
||||
*/
|
||||
void BLI_path_sequence_encode(char *path,
|
||||
size_t path_maxncpy,
|
||||
const char *head,
|
||||
const char *tail,
|
||||
unsigned short numlen,
|
||||
int pic);
|
||||
|
||||
/**
|
||||
* Remove redundant characters from \a path.
|
||||
*
|
||||
* The following operations are performed:
|
||||
* - Redundant path components such as `//`, `/./` & `./` (prefix) are stripped.
|
||||
* (with the exception of `//` prefix used for blend-file relative paths).
|
||||
* - `..` are resolved so `<parent>/../<child>/` resolves to `<child>/`.
|
||||
* Note that the resulting path may begin with `..` if it's relative.
|
||||
*
|
||||
* Details:
|
||||
* - The slash direction is expected to be native (see #SEP).
|
||||
* When calculating a canonical paths you may need to run #BLI_path_slash_native first.
|
||||
* #BLI_path_cmp_normalized can be used for canonical path comparison.
|
||||
* - Trailing slashes are left intact (unlike Python which strips them).
|
||||
* - Handling paths beginning with `..` depends on them being absolute or relative.
|
||||
* For absolute paths they are removed (e.g. `/../path` becomes `/path`).
|
||||
* For relative paths they are kept as it's valid to reference paths above a relative location
|
||||
* such as `//../parent` or `../parent`.
|
||||
*
|
||||
* \param path: The path to a file or directory which can be absolute or relative.
|
||||
*/
|
||||
void BLI_path_normalize(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Cleanup file-path ensuring a trailing slash.
|
||||
*
|
||||
* \note Same as #BLI_path_normalize but adds a trailing slash.
|
||||
*/
|
||||
void BLI_path_normalize_dir(char *dir, size_t dir_maxncpy) ATTR_NONNULL(1);
|
||||
/** \} */
|
||||
|
||||
/**
|
||||
* Make given name safe to be used in paths.
|
||||
*
|
||||
* \param allow_tokens: Permit the usage of '<' and '>' characters. This can be
|
||||
* leveraged by higher layers to support "virtual filenames" which contain
|
||||
* substitution markers delineated between the two characters.
|
||||
*
|
||||
* \return true if \a fname was changed, false otherwise.
|
||||
*
|
||||
* For now, simply replaces reserved chars (as listed in
|
||||
* https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words )
|
||||
* by underscores ('_').
|
||||
*
|
||||
* \note Space case ' ' is a bit of an edge case here - in theory it is allowed,
|
||||
* but again can be an issue in some cases, so we simply replace it by an underscore too
|
||||
* (good practice anyway).
|
||||
* REMOVED based on popular demand (see #45900).
|
||||
* Percent '%' char is a bit same case - not recommended to use it,
|
||||
* but supported by all decent file-systems/operating-systems around.
|
||||
*
|
||||
* \note On Windows, it also ensures there is no '.' (dot char) at the end of the file,
|
||||
* this can lead to issues.
|
||||
*
|
||||
* \note On Windows, it also checks for forbidden names
|
||||
* (see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx ).
|
||||
*/
|
||||
bool BLI_path_make_safe_filename_ex(char *fname, bool allow_tokens) ATTR_NONNULL(1);
|
||||
bool BLI_path_make_safe_filename(char *fname) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Make given path OS-safe.
|
||||
*
|
||||
* \return true if \a path was changed, false otherwise.
|
||||
*/
|
||||
bool BLI_path_make_safe(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Go back one directory.
|
||||
*
|
||||
* Replaces path with the path of its parent directory, returning true if
|
||||
* it was able to find a parent directory within the path.
|
||||
*
|
||||
* On success, the resulting path will always have a trailing slash.
|
||||
*/
|
||||
bool BLI_path_parent_dir(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Go back until the directory is found.
|
||||
*
|
||||
* Strips off nonexistent (or non-accessible) sub-directories from the end of `dir`,
|
||||
* leaving the path of the lowest-level directory that does exist and we can read.
|
||||
*/
|
||||
bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* In the simple case this is similar to `BLI_path_slash_rfind(dirname)`
|
||||
* however it behaves differently when there are redundant characters:
|
||||
*
|
||||
* `/test///dir/./file`
|
||||
* ^
|
||||
* `/test/dir/subdir//file`
|
||||
* ^
|
||||
* \return The position after the parent paths last character or NULL on failure.
|
||||
* Neither `path` or `&path[path_len - 1]` are ever returned.
|
||||
*/
|
||||
const char *BLI_path_parent_dir_end(const char *path, size_t path_len)
|
||||
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* If path begins with "//", strips that and replaces it with `basepath` directory.
|
||||
*
|
||||
* \note Also converts drive-letter prefix to something more sensible
|
||||
* if this is a non-drive-letter-based system.
|
||||
*
|
||||
* \param path: The path to convert.
|
||||
* \param basepath: The directory to base relative paths with.
|
||||
* \return true if the path was relative (started with "//").
|
||||
*/
|
||||
bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL(1, 2);
|
||||
/**
|
||||
* Replaces "#" character sequence in last slash-separated component of `path`
|
||||
* with frame as decimal integer, with leading zeroes as necessary, to make digits.
|
||||
*/
|
||||
bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Replaces "#" character sequence in last slash-separated component of `path`
|
||||
* with sta and end as decimal integers, with leading zeroes as necessary, to make digits
|
||||
* digits each, with a hyphen in-between.
|
||||
*/
|
||||
bool BLI_path_frame_range(char *path, int sta, int end, int digits) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Get the frame from a filename formatted by blender's frame scheme
|
||||
*/
|
||||
bool BLI_path_frame_get(const char *path, int *r_frame, int *r_digits_len) ATTR_NONNULL(1, 2, 3);
|
||||
/**
|
||||
* Given a `path` with digits representing frame numbers, replace the digits with the '#'
|
||||
* character and extract the extension.
|
||||
* So: `/some/path_123.jpeg`
|
||||
* Becomes: `/some/path_###` with `r_ext` set to `.jpeg`.
|
||||
*/
|
||||
void BLI_path_frame_strip(char *path, char *r_ext, size_t ext_maxncpy) ATTR_NONNULL(1, 2);
|
||||
/**
|
||||
* Check if we have '#' chars, usable for #BLI_path_frame, #BLI_path_frame_range
|
||||
*/
|
||||
bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Checks for a relative path (ignoring Blender's "//") prefix
|
||||
* (unlike `!BLI_path_is_rel(path)`).
|
||||
* When false, #BLI_path_abs_from_cwd would expand the absolute path.
|
||||
*/
|
||||
bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Checks for relative path, expanding them relative to the current working directory.
|
||||
* \returns true if the expansion was performed.
|
||||
*
|
||||
* \note Should only be called with command line paths.
|
||||
* This is _not_ something Blender's internal paths support, instead they use the "//" prefix.
|
||||
* In most cases #BLI_path_abs should be used instead.
|
||||
*/
|
||||
bool BLI_path_abs_from_cwd(char *path, size_t path_maxncpy) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Replaces `file` with a relative version (prefixed by "//") such that #BLI_path_abs, given
|
||||
* the same `basename`, will convert it back to its original value.
|
||||
*/
|
||||
void BLI_path_rel(char *path, const char *basename) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Does path begin with the special "//" prefix that Blender uses to indicate
|
||||
* a path relative to the .blend file.
|
||||
*/
|
||||
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Return true if the path is a UNC share.
|
||||
*/
|
||||
bool BLI_path_is_unc(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Creates a display string from path to be used menus and the user interface.
|
||||
* Like `bpy.path.display_name()`.
|
||||
*/
|
||||
void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name)
|
||||
ATTR_NONNULL(1, 3);
|
||||
|
||||
#if defined(WIN32)
|
||||
void BLI_path_normalize_unc_16(wchar_t *path_16);
|
||||
void BLI_path_normalize_unc(char *path, int path_maxncpy);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Appends a suffix to the `path`, fitting it before the extension
|
||||
*
|
||||
* path = `Foo.png`, suffix = `123`, separator = `_`.
|
||||
* `Foo.png` -> `Foo_123.png`.
|
||||
*
|
||||
* \param path: original (and final) string.
|
||||
* \param path_maxncpy: Maximum length of path.
|
||||
* \param suffix: String to append to the original path.
|
||||
* \param sep: Optional separator character.
|
||||
* \return true if succeeded.
|
||||
*/
|
||||
bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const char *sep)
|
||||
ATTR_NONNULL(1, 3, 4);
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Comparison / Contains
|
||||
* \{ */
|
||||
|
||||
/* Path string comparisons: case-insensitive for Windows, case-sensitive otherwise. */
|
||||
#if defined(WIN32)
|
||||
|
@ -543,14 +484,172 @@ bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const
|
|||
int BLI_path_cmp_normalized(const char *p1, const char *p2)
|
||||
ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/* These values need to be hard-coded in structs, dna does not recognize defines */
|
||||
/* also defined in `DNA_space_types.h`. */
|
||||
/** Return true only if #containee_path is contained in #container_path. */
|
||||
bool BLI_path_contains(const char *container_path, const char *containee_path)
|
||||
ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Program Specific Path Functions
|
||||
* \{ */
|
||||
|
||||
#ifdef _WIN32
|
||||
bool BLI_path_program_extensions_add_win32(char *program_name, size_t program_name_maxncpy);
|
||||
#endif
|
||||
/**
|
||||
* Search for a binary (executable)
|
||||
*/
|
||||
bool BLI_path_program_search(char *program_filepath,
|
||||
size_t program_filepath_maxncpy,
|
||||
const char *program_name) ATTR_NONNULL(1, 3);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blender Specific Frame Sequence Encode/Decode
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Returns in area pointed to by `path` a string of the form `<head><pic><tail>`,
|
||||
* where pic is formatted as `numlen` digits with leading zeroes.
|
||||
*/
|
||||
void BLI_path_sequence_encode(char *path,
|
||||
size_t path_maxncpy,
|
||||
const char *head,
|
||||
const char *tail,
|
||||
unsigned short numlen,
|
||||
int pic);
|
||||
|
||||
/**
|
||||
* Looks for a sequence of decimal digits in `path`, preceding any filename extension,
|
||||
* returning the integer value if found, or 0 if not.
|
||||
*
|
||||
* \param path: String to scan.
|
||||
* \param head: Optional area to return copy of part of `path` prior to digits,
|
||||
* or before dot if no digits.
|
||||
* \param tail: Optional area to return copy of part of `path` following digits,
|
||||
* or from dot if no digits.
|
||||
* \param r_digits_len: Optional to return number of digits found.
|
||||
*/
|
||||
int BLI_path_sequence_decode(const char *path,
|
||||
char *head,
|
||||
size_t head_maxncpy,
|
||||
char *tail,
|
||||
size_t tail_maxncpy,
|
||||
unsigned short *r_digits_len);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blender Specific Frame Number Apply/Strip
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Replaces "#" character sequence in last slash-separated component of `path`
|
||||
* with frame as decimal integer, with leading zeroes as necessary, to make digits.
|
||||
*/
|
||||
bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Replaces "#" character sequence in last slash-separated component of `path`
|
||||
* with sta and end as decimal integers, with leading zeroes as necessary, to make digits
|
||||
* digits each, with a hyphen in-between.
|
||||
*/
|
||||
bool BLI_path_frame_range(char *path, size_t path_maxncpy, int sta, int end, int digits)
|
||||
ATTR_NONNULL(1);
|
||||
/**
|
||||
* Get the frame from a filename formatted by blender's frame scheme
|
||||
*/
|
||||
bool BLI_path_frame_get(const char *path, int *r_frame, int *r_digits_len) ATTR_NONNULL(1, 2, 3);
|
||||
/**
|
||||
* Given a `path` with digits representing frame numbers, replace the digits with the '#'
|
||||
* character and extract the extension.
|
||||
* So: `/some/path_123.jpeg`
|
||||
* Becomes: `/some/path_###` with `r_ext` set to `.jpeg`.
|
||||
*/
|
||||
void BLI_path_frame_strip(char *path, char *r_ext, size_t ext_maxncpy) ATTR_NONNULL(1, 2);
|
||||
/**
|
||||
* Check if we have '#' chars, usable for #BLI_path_frame, #BLI_path_frame_range
|
||||
*/
|
||||
bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blender Spesific File Relative Paths
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* These values need to be hard-coded in structs, dna does not recognize defines
|
||||
* (also defined in `DNA_space_types.h`).
|
||||
*
|
||||
* \note In general path functions should *not* depend on these hard coded limits,
|
||||
* there is an exception for:
|
||||
* - #BLI_path_abs
|
||||
* - #BLI_path_rel
|
||||
* These functions deal specifically with `.blend` file paths,
|
||||
* where #FILE_MAX assumed to be the limit of all paths passes into these functions.
|
||||
*
|
||||
* Some parts of the API which use #FILE_MAX which aren't specifically handling blend file paths,
|
||||
* in most cases these can be updated to use #PATH_MAX or a platform specific limit.
|
||||
*/
|
||||
#ifndef FILE_MAXDIR
|
||||
# define FILE_MAXDIR 768
|
||||
# define FILE_MAXFILE 256
|
||||
# define FILE_MAX 1024
|
||||
#endif
|
||||
|
||||
/**
|
||||
* If path begins with "//", strips that and replaces it with `basepath` directory.
|
||||
*
|
||||
* \note Also converts drive-letter prefix to something more sensible
|
||||
* if this is a non-drive-letter-based system.
|
||||
*
|
||||
* \param path: The path to convert.
|
||||
* \param basepath: The directory to base relative paths with.
|
||||
* \return true if the path was relative (started with "//").
|
||||
*/
|
||||
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1, 2);
|
||||
/**
|
||||
* Replaces `path` with a relative version (prefixed by "//") such that #BLI_path_abs, given
|
||||
* the same `basepath`, will convert it back to its original value.
|
||||
*/
|
||||
void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Does path begin with the special "//" prefix that Blender uses to indicate
|
||||
* a path relative to the .blend file.
|
||||
*/
|
||||
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Current Working Directory Specific Paths
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Checks for a relative path (ignoring Blender's "//") prefix
|
||||
* (unlike `!BLI_path_is_rel(path)`).
|
||||
* When false, #BLI_path_abs_from_cwd would expand the absolute path.
|
||||
*/
|
||||
bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* Checks for relative path, expanding them relative to the current working directory.
|
||||
* \returns true if the expansion was performed.
|
||||
*
|
||||
* \note Should only be called with command line paths.
|
||||
* This is _not_ something Blender's internal paths support, instead they use the "//" prefix.
|
||||
* In most cases #BLI_path_abs should be used instead.
|
||||
*/
|
||||
bool BLI_path_abs_from_cwd(char *path, size_t path_maxncpy) ATTR_NONNULL(1);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Native Slash Defines & Checks
|
||||
* \{ */
|
||||
|
||||
#ifdef WIN32
|
||||
# define SEP '\\'
|
||||
# define ALTSEP '/'
|
||||
|
@ -581,6 +680,42 @@ BLI_INLINE bool BLI_path_slash_is_native_compat(const char ch)
|
|||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name OS Level Wrappers
|
||||
*
|
||||
* TODO: move these to a different module, they are not path functions.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Sets the specified environment variable to the specified value,
|
||||
* and clears it if `val == NULL`.
|
||||
*/
|
||||
void BLI_setenv(const char *env, const char *val) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Only set an environment variable if already not there.
|
||||
* Like Unix `setenv(env, val, 0);`
|
||||
*
|
||||
* (not used anywhere).
|
||||
*/
|
||||
void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Get an environment variable, result has to be used immediately.
|
||||
*
|
||||
* On windows #getenv gets its variables from a static copy of the environment variables taken at
|
||||
* process start-up, causing it to not pick up on environment variables created during runtime.
|
||||
* This function uses an alternative method to get environment variables that does pick up on
|
||||
* runtime environment variables. The result will be UTF-8 encoded.
|
||||
*/
|
||||
const char *BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Current & Parent Directory Defines/Macros
|
||||
* \{ */
|
||||
|
||||
/* Parent and current dir helpers. */
|
||||
#define FILENAME_PARENT ".."
|
||||
#define FILENAME_CURRENT "."
|
||||
|
@ -591,6 +726,8 @@ BLI_INLINE bool BLI_path_slash_is_native_compat(const char ch)
|
|||
#define FILENAME_IS_CURRPAR(_n) \
|
||||
(((_n)[0] == '.') && (((_n)[1] == '\0') || (((_n)[1] == '.') && ((_n)[2] == '\0'))))
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -191,6 +191,15 @@ bool BLI_str_replace_table_exact(char *string,
|
|||
const char *replace_table[][2],
|
||||
int replace_table_len);
|
||||
|
||||
/**
|
||||
* Write `dst` into the range between `src_beg` & `src_end`,
|
||||
* resize within `string_maxncpy` limits, ensure null terminated.
|
||||
*
|
||||
* \return the length of `string`.
|
||||
*/
|
||||
size_t BLI_str_replace_range(
|
||||
char *string, size_t string_maxncpy, int src_beg, int src_end, const char *dst);
|
||||
|
||||
/**
|
||||
* Portable replacement for #snprintf
|
||||
*/
|
||||
|
|
|
@ -42,6 +42,26 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
|
|||
eStrCursorJumpType jump,
|
||||
bool use_init_step);
|
||||
|
||||
/**
|
||||
* Given a position within a string,
|
||||
* return the start and end of the closest sequence of delimited characters.
|
||||
* Typically a word, but can be a sequence of characters (including spaces).
|
||||
*
|
||||
* \note When used for word-selection the caller should set the cursor to `r_end` (by convention).
|
||||
*
|
||||
* \param str: The string with a cursor position
|
||||
* \param str_maxlen: The maximum characters to consider
|
||||
* \param pos: The starting cursor position.
|
||||
* \param r_start: returned start of word/sequence boundary (0-based)
|
||||
* \param r_end: returned end of word/sequence boundary (0-based)
|
||||
*/
|
||||
void BLI_str_cursor_step_bounds_utf8(
|
||||
const char *str, const size_t str_maxlen, int pos, int *r_start, int *r_end);
|
||||
|
||||
/** A UTF32 version of #BLI_str_cursor_step_bounds_utf8 */
|
||||
void BLI_str_cursor_step_bounds_utf32(
|
||||
const char32_t *str, const size_t str_maxlen, int pos, int *r_start, int *r_end);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -560,7 +560,7 @@ static bool delete_recursive(const char *dir)
|
|||
char path[FILE_MAXDIR];
|
||||
|
||||
/* dir listing produces dir path without trailing slash... */
|
||||
BLI_strncpy(path, fl->path, sizeof(path));
|
||||
STRNCPY(path, fl->path);
|
||||
BLI_path_slash_ensure(path, sizeof(path));
|
||||
|
||||
if (delete_recursive(path)) {
|
||||
|
@ -627,7 +627,7 @@ int BLI_path_move(const char *file, const char *to)
|
|||
* it has to be 'mv filepath filepath' and not
|
||||
* 'mv filepath destination_directory' */
|
||||
|
||||
BLI_strncpy(str, to, sizeof(str));
|
||||
STRNCPY(str, to);
|
||||
/* points 'to' to a directory ? */
|
||||
if (BLI_path_slash_rfind(str) == (str + strlen(str) - 1)) {
|
||||
if (BLI_path_slash_rfind(file) != NULL) {
|
||||
|
@ -658,7 +658,7 @@ int BLI_copy(const char *file, const char *to)
|
|||
* it has to be 'cp filepath filepath' and not
|
||||
* 'cp filepath destdir' */
|
||||
|
||||
BLI_strncpy(str, to, sizeof(str));
|
||||
STRNCPY(str, to);
|
||||
/* points 'to' to a directory ? */
|
||||
if (BLI_path_slash_rfind(str) == (str + strlen(str) - 1)) {
|
||||
if (BLI_path_slash_rfind(file) != NULL) {
|
||||
|
@ -741,9 +741,9 @@ static void join_dirfile_alloc(char **dst, size_t *alloc_len, const char *dir, c
|
|||
BLI_path_join(*dst, len + 1, dir, file);
|
||||
}
|
||||
|
||||
static char *strip_last_slash(const char *dir)
|
||||
static char *strip_last_slash(const char *dirpath)
|
||||
{
|
||||
char *result = BLI_strdup(dir);
|
||||
char *result = BLI_strdup(dirpath);
|
||||
BLI_path_slash_rstrip(result);
|
||||
|
||||
return result;
|
||||
|
|
|
@ -871,6 +871,47 @@ void BLI_listbase_rotate_last(ListBase *lb, void *vlink)
|
|||
((Link *)lb->last)->next = nullptr;
|
||||
}
|
||||
|
||||
bool BLI_listbase_validate(ListBase *lb)
|
||||
{
|
||||
if (lb->first == nullptr && lb->last == nullptr) {
|
||||
/* Empty list. */
|
||||
return true;
|
||||
}
|
||||
if (ELEM(nullptr, lb->first, lb->last)) {
|
||||
/* If one of the pointer is null, but not this other, this is a corrupted listbase. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Walk the list in bot directions to ensure all next & prev pointers are valid and consistent.
|
||||
*/
|
||||
for (Link *lb_link = static_cast<Link *>(lb->first); lb_link; lb_link = lb_link->next) {
|
||||
if (lb_link == lb->first) {
|
||||
if (lb_link->prev != nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (lb_link == lb->last) {
|
||||
if (lb_link->next != nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Link *lb_link = static_cast<Link *>(lb->last); lb_link; lb_link = lb_link->prev) {
|
||||
if (lb_link == lb->last) {
|
||||
if (lb_link->next != nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (lb_link == lb->first) {
|
||||
if (lb_link->prev != nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LinkData *BLI_genericNodeN(void *data)
|
||||
{
|
||||
LinkData *ld;
|
||||
|
|
|
@ -45,6 +45,14 @@ static int BLI_path_unc_prefix_len(const char *path);
|
|||
static bool BLI_path_is_abs_win32(const char *path);
|
||||
#endif /* WIN32 */
|
||||
|
||||
/**
|
||||
* The maximum number of `#` characters expanded for #BLI_path_frame & #BLI_path_frame_range
|
||||
* Typically 12 is enough and even 16 is very large.
|
||||
* Use a much larger value so hitting the upper limit is not an issue.
|
||||
* Exceeding this limit won't fail either, it will just not insert so many leading zeros.
|
||||
*/
|
||||
#define FILENAME_FRAME_CHARS_MAX FILE_MAX
|
||||
|
||||
int BLI_path_sequence_decode(const char *path,
|
||||
char *head,
|
||||
const size_t head_maxncpy,
|
||||
|
@ -89,7 +97,7 @@ int BLI_path_sequence_decode(const char *path,
|
|||
const long long int ret = strtoll(&(path[nums]), NULL, 10);
|
||||
if (ret >= INT_MIN && ret <= INT_MAX) {
|
||||
if (tail) {
|
||||
strcpy(tail, &path[nume + 1]);
|
||||
BLI_strncpy(tail, &path[nume + 1], tail_maxncpy);
|
||||
}
|
||||
if (head) {
|
||||
BLI_strncpy(head, path, MIN2(head_maxncpy, nums + 1));
|
||||
|
@ -584,7 +592,7 @@ void BLI_path_normalize_unc_16(wchar_t *path_16)
|
|||
}
|
||||
#endif
|
||||
|
||||
void BLI_path_rel(char *path, const char *basename)
|
||||
void BLI_path_rel(char path[FILE_MAX], const char *basepath)
|
||||
{
|
||||
BLI_string_debug_size_after_nil(path, FILE_MAX);
|
||||
|
||||
|
@ -598,24 +606,24 @@ void BLI_path_rel(char *path, const char *basename)
|
|||
}
|
||||
|
||||
/* Also bail out if relative path is not set. */
|
||||
if (basename[0] == '\0') {
|
||||
if (basepath[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
if (BLI_strnlen(basename, 3) > 2 && !BLI_path_is_abs_win32(basename)) {
|
||||
if (BLI_strnlen(basepath, 3) > 2 && !BLI_path_is_abs_win32(basepath)) {
|
||||
char *ptemp;
|
||||
/* Fix missing volume name in relative base,
|
||||
* can happen with old `recent-files.txt` files. */
|
||||
BLI_windows_get_default_root_dir(temp);
|
||||
ptemp = &temp[2];
|
||||
if (!ELEM(basename[0], '\\', '/')) {
|
||||
if (!ELEM(basepath[0], '\\', '/')) {
|
||||
ptemp++;
|
||||
}
|
||||
BLI_strncpy(ptemp, basename, FILE_MAX - 3);
|
||||
BLI_strncpy(ptemp, basepath, FILE_MAX - 3);
|
||||
}
|
||||
else {
|
||||
BLI_strncpy(temp, basename, FILE_MAX);
|
||||
BLI_strncpy(temp, basepath, FILE_MAX);
|
||||
}
|
||||
|
||||
if (BLI_strnlen(path, 3) > 2) {
|
||||
|
@ -645,7 +653,7 @@ void BLI_path_rel(char *path, const char *basename)
|
|||
}
|
||||
}
|
||||
#else
|
||||
STRNCPY(temp, basename);
|
||||
STRNCPY(temp, basepath);
|
||||
#endif
|
||||
|
||||
BLI_str_replace_char(temp + BLI_path_unc_prefix_len(temp), '\\', '/');
|
||||
|
@ -819,9 +827,14 @@ bool BLI_path_parent_dir_until_exists(char *dir)
|
|||
/**
|
||||
* Looks for a sequence of "#" characters in the last slash-separated component of `path`,
|
||||
* returning the indexes of the first and one past the last character in the sequence in
|
||||
* `char_start` and `char_end` respectively. Returns true if such a sequence was found.
|
||||
* `char_start` and `char_end` respectively.
|
||||
*
|
||||
* \param char_start: The first `#` character.
|
||||
* \param char_end: The last `#` character +1.
|
||||
*
|
||||
* \return true if a frame sequence range was found.
|
||||
*/
|
||||
static bool stringframe_chars(const char *path, int *char_start, int *char_end)
|
||||
static bool path_frame_chars_find_range(const char *path, int *char_start, int *char_end)
|
||||
{
|
||||
uint ch_sta, ch_end, i;
|
||||
/* Insert current frame: `file###` -> `file001`. */
|
||||
|
@ -885,18 +898,19 @@ bool BLI_path_frame(char *path, size_t path_maxncpy, int frame, int digits)
|
|||
ensure_digits(path, digits);
|
||||
}
|
||||
|
||||
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* Warning: `ch_end` is the last # +1. */
|
||||
char tmp[FILE_MAX];
|
||||
SNPRINTF(tmp, "%.*s%.*d%s", ch_sta, path, ch_end - ch_sta, frame, path + ch_end);
|
||||
BLI_strncpy(path, tmp, path_maxncpy);
|
||||
if (path_frame_chars_find_range(path, &ch_sta, &ch_end)) {
|
||||
char frame_str[FILENAME_FRAME_CHARS_MAX + 1]; /* One for null. */
|
||||
const int ch_span = MIN2(ch_end - ch_sta, FILENAME_FRAME_CHARS_MAX);
|
||||
BLI_snprintf(frame_str, sizeof(frame_str), "%.*d", ch_span, frame);
|
||||
BLI_str_replace_range(path, path_maxncpy, ch_sta, ch_end, frame_str);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BLI_path_frame_range(char *path, int sta, int end, int digits)
|
||||
bool BLI_path_frame_range(char *path, size_t path_maxncpy, int sta, int end, int digits)
|
||||
{
|
||||
BLI_string_debug_size_after_nil(path, FILE_MAX);
|
||||
BLI_string_debug_size_after_nil(path, path_maxncpy);
|
||||
|
||||
int ch_sta, ch_end;
|
||||
|
||||
|
@ -904,18 +918,11 @@ bool BLI_path_frame_range(char *path, int sta, int end, int digits)
|
|||
ensure_digits(path, digits);
|
||||
}
|
||||
|
||||
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* Warning: `ch_end` is the last # +1. */
|
||||
char tmp[FILE_MAX];
|
||||
SNPRINTF(tmp,
|
||||
"%.*s%.*d-%.*d%s",
|
||||
ch_sta,
|
||||
path,
|
||||
ch_end - ch_sta,
|
||||
sta,
|
||||
ch_end - ch_sta,
|
||||
end,
|
||||
path + ch_end);
|
||||
BLI_strncpy(path, tmp, FILE_MAX);
|
||||
if (path_frame_chars_find_range(path, &ch_sta, &ch_end)) {
|
||||
char frame_str[(FILENAME_FRAME_CHARS_MAX * 2) + 1 + 1]; /* One for null, one for the '-' */
|
||||
const int ch_span = MIN2(ch_end - ch_sta, FILENAME_FRAME_CHARS_MAX);
|
||||
BLI_snprintf(frame_str, sizeof(frame_str), "%.*d-%.*d", ch_span, sta, ch_span, end);
|
||||
BLI_str_replace_range(path, path_maxncpy, ch_sta, ch_end, frame_str);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -981,7 +988,7 @@ void BLI_path_frame_strip(char *path, char *r_ext, const size_t ext_maxncpy)
|
|||
bool BLI_path_frame_check_chars(const char *path)
|
||||
{
|
||||
int ch_sta_dummy, ch_end_dummy;
|
||||
return stringframe_chars(path, &ch_sta_dummy, &ch_end_dummy);
|
||||
return path_frame_chars_find_range(path, &ch_sta_dummy, &ch_end_dummy);
|
||||
}
|
||||
|
||||
void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name)
|
||||
|
@ -1023,7 +1030,7 @@ void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, cons
|
|||
}
|
||||
}
|
||||
|
||||
bool BLI_path_abs(char *path, const char *basepath)
|
||||
bool BLI_path_abs(char path[FILE_MAX], const char *basepath)
|
||||
{
|
||||
BLI_string_debug_size_after_nil(path, FILE_MAX);
|
||||
|
||||
|
@ -1146,10 +1153,10 @@ bool BLI_path_abs_from_cwd(char *path, const size_t path_maxncpy)
|
|||
BLI_string_debug_size_after_nil(path, path_maxncpy);
|
||||
|
||||
if (!BLI_path_is_abs_from_cwd(path)) {
|
||||
char cwd[FILE_MAX];
|
||||
char cwd[PATH_MAX];
|
||||
/* In case the full path to the blend isn't used. */
|
||||
if (BLI_current_working_dir(cwd, sizeof(cwd))) {
|
||||
char origpath[FILE_MAX];
|
||||
char origpath[PATH_MAX];
|
||||
STRNCPY(origpath, path);
|
||||
BLI_path_join(path, path_maxncpy, cwd, origpath);
|
||||
}
|
||||
|
@ -1232,7 +1239,7 @@ bool BLI_path_program_search(char *program_filepath,
|
|||
|
||||
path = BLI_getenv("PATH");
|
||||
if (path) {
|
||||
char filepath_test[FILE_MAX];
|
||||
char filepath_test[PATH_MAX];
|
||||
const char *temp;
|
||||
|
||||
do {
|
||||
|
@ -1484,13 +1491,13 @@ bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *file)
|
||||
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename)
|
||||
{
|
||||
BLI_string_debug_size_after_nil(filepath, filepath_maxncpy);
|
||||
|
||||
char *c = (char *)BLI_path_slash_rfind(filepath);
|
||||
if (!c || ((c - filepath) < filepath_maxncpy - (strlen(file) + 1))) {
|
||||
strcpy(c ? &c[1] : filepath, file);
|
||||
char *c = (char *)BLI_path_basename(filepath);
|
||||
const size_t filename_size = strlen(filename) + 1;
|
||||
if (filename_size <= filepath_maxncpy - (c - filepath)) {
|
||||
memcpy(c, filename, filename_size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1911,11 +1918,17 @@ int BLI_path_cmp_normalized(const char *p1, const char *p2)
|
|||
BLI_assert_msg(!BLI_path_is_rel(p1) && !BLI_path_is_rel(p2), "Paths arguments must be absolute");
|
||||
|
||||
/* Normalize the paths so we can compare them. */
|
||||
char norm_p1[FILE_MAX];
|
||||
char norm_p2[FILE_MAX];
|
||||
char norm_p1_buf[256];
|
||||
char norm_p2_buf[256];
|
||||
|
||||
STRNCPY(norm_p1, p1);
|
||||
STRNCPY(norm_p2, p2);
|
||||
const size_t p1_size = strlen(p1) + 1;
|
||||
const size_t p2_size = strlen(p2) + 1;
|
||||
|
||||
char *norm_p1 = (p1_size <= sizeof(norm_p1_buf)) ? norm_p1_buf : MEM_mallocN(p1_size, __func__);
|
||||
char *norm_p2 = (p2_size <= sizeof(norm_p2_buf)) ? norm_p2_buf : MEM_mallocN(p2_size, __func__);
|
||||
|
||||
memcpy(norm_p1, p1, p1_size);
|
||||
memcpy(norm_p2, p2, p2_size);
|
||||
|
||||
BLI_path_slash_native(norm_p1);
|
||||
BLI_path_slash_native(norm_p2);
|
||||
|
@ -1923,5 +1936,13 @@ int BLI_path_cmp_normalized(const char *p1, const char *p2)
|
|||
BLI_path_normalize(norm_p1);
|
||||
BLI_path_normalize(norm_p2);
|
||||
|
||||
return BLI_path_cmp(norm_p1, norm_p2);
|
||||
const int result = BLI_path_cmp(norm_p1, norm_p2);
|
||||
|
||||
if (norm_p1 != norm_p1_buf) {
|
||||
MEM_freeN(norm_p1);
|
||||
}
|
||||
if (norm_p2 != norm_p2_buf) {
|
||||
MEM_freeN(norm_p2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -552,6 +552,55 @@ bool BLI_str_replace_table_exact(char *string,
|
|||
return false;
|
||||
}
|
||||
|
||||
size_t BLI_str_replace_range(
|
||||
char *string, size_t string_maxncpy, int src_beg, int src_end, const char *dst)
|
||||
{
|
||||
int string_len = strlen(string);
|
||||
BLI_assert(src_beg <= src_end);
|
||||
BLI_assert(src_end <= string_len);
|
||||
const int src_len = src_end - src_beg;
|
||||
int dst_len = strlen(dst);
|
||||
|
||||
if (src_len < dst_len) {
|
||||
/* Grow, first handle special cases. */
|
||||
|
||||
/* Special case, the src_end is entirely clipped. */
|
||||
if (UNLIKELY(string_maxncpy <= src_beg + dst_len)) {
|
||||
/* There is only room for the destination. */
|
||||
dst_len = ((int)string_maxncpy - src_beg) - 1;
|
||||
string_len = src_end;
|
||||
string[string_len] = '\0';
|
||||
}
|
||||
|
||||
const int ofs = dst_len - src_len;
|
||||
/* Clip the string when inserting the destination string exceeds `string_maxncpy`. */
|
||||
if (string_len + ofs >= string_maxncpy) {
|
||||
string_len = ((int)string_maxncpy - ofs) - 1;
|
||||
string[string_len] = '\0';
|
||||
BLI_assert(src_end <= string_len);
|
||||
}
|
||||
|
||||
/* Grow. */
|
||||
memmove(string + (src_end + ofs), string + src_end, (size_t)(string_len - src_end) + 1);
|
||||
string_len += ofs;
|
||||
}
|
||||
else if (src_len > dst_len) {
|
||||
/* Shrink. */
|
||||
const int ofs = src_len - dst_len;
|
||||
memmove(string + (src_end - ofs), string + src_end, (size_t)(string_len - src_end) + 1);
|
||||
string_len -= ofs;
|
||||
}
|
||||
else { /* Simple case, no resizing. */
|
||||
BLI_assert(src_len == dst_len);
|
||||
}
|
||||
|
||||
if (dst_len > 0) {
|
||||
memcpy(string + src_beg, dst, (size_t)dst_len);
|
||||
}
|
||||
BLI_assert(string[string_len] == '\0');
|
||||
return (size_t)string_len;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -17,6 +17,18 @@
|
|||
# pragma GCC diagnostic error "-Wsign-conversion"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The category of character as returned by #cursor_delim_type_unicode.
|
||||
*
|
||||
* \note Don't compare with any values besides #STRCUR_DELIM_NONE as cursor motion
|
||||
* should only delimit on changes, not treat some groups differently.
|
||||
*
|
||||
* For range calculation the order prioritizes expansion direction,
|
||||
* when the cursor is between two different categories, "hug" the smaller values.
|
||||
* Where white-space gets lowest priority. See #BLI_str_cursor_step_bounds_utf8.
|
||||
* This is done so expanding the range at a word boundary always chooses the word instead
|
||||
* of the white-space before or after it.
|
||||
*/
|
||||
typedef enum eStrCursorDelimType {
|
||||
STRCUR_DELIM_NONE,
|
||||
STRCUR_DELIM_ALPHANUMERIC,
|
||||
|
@ -24,8 +36,8 @@ typedef enum eStrCursorDelimType {
|
|||
STRCUR_DELIM_BRACE,
|
||||
STRCUR_DELIM_OPERATOR,
|
||||
STRCUR_DELIM_QUOTE,
|
||||
STRCUR_DELIM_WHITESPACE,
|
||||
STRCUR_DELIM_OTHER,
|
||||
STRCUR_DELIM_WHITESPACE,
|
||||
} eStrCursorDelimType;
|
||||
|
||||
static eStrCursorDelimType cursor_delim_type_unicode(const uint uch)
|
||||
|
@ -314,3 +326,46 @@ void BLI_str_cursor_step_utf32(const char32_t *str,
|
|||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
void BLI_str_cursor_step_bounds_utf8(
|
||||
const char *str, const size_t str_maxlen, const int pos, int *r_start, int *r_end)
|
||||
{
|
||||
/* Identify the type of characters are on either side of the current cursor position. */
|
||||
const eStrCursorDelimType prev = (pos > 0) ? cursor_delim_type_utf8(str, str_maxlen, pos - 1) :
|
||||
STRCUR_DELIM_NONE;
|
||||
const eStrCursorDelimType next = (pos < str_maxlen) ?
|
||||
cursor_delim_type_utf8(str, str_maxlen, pos) :
|
||||
STRCUR_DELIM_NONE;
|
||||
*r_start = pos;
|
||||
*r_end = pos;
|
||||
|
||||
if ((prev <= next) && (prev != STRCUR_DELIM_NONE)) {
|
||||
/* Expand backward if we are between similar content. */
|
||||
BLI_str_cursor_step_utf8(str, str_maxlen, r_start, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, false);
|
||||
}
|
||||
if ((prev >= next) && (next != STRCUR_DELIM_NONE)) {
|
||||
/* Expand forward if we are between similar content, after whitespace, or at beginning. */
|
||||
BLI_str_cursor_step_utf8(str, str_maxlen, r_end, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, false);
|
||||
}
|
||||
}
|
||||
|
||||
void BLI_str_cursor_step_bounds_utf32(
|
||||
const char32_t *str, const size_t str_maxlen, const int pos, int *r_start, int *r_end)
|
||||
{
|
||||
/* Identify the type of characters are on either side of the current cursor position. */
|
||||
const eStrCursorDelimType prev = (pos > 0) ? cursor_delim_type_unicode(str[pos - 1]) :
|
||||
STRCUR_DELIM_NONE;
|
||||
const eStrCursorDelimType next = (pos < str_maxlen) ? cursor_delim_type_unicode(str[pos]) :
|
||||
STRCUR_DELIM_NONE;
|
||||
*r_start = pos;
|
||||
*r_end = pos;
|
||||
|
||||
if ((prev <= next) && (prev != STRCUR_DELIM_NONE)) {
|
||||
/* Expand backward if we are between similar content, before whitespace, or at end. */
|
||||
BLI_str_cursor_step_utf32(str, str_maxlen, r_start, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, false);
|
||||
}
|
||||
if ((prev >= next) && (next != STRCUR_DELIM_NONE)) {
|
||||
/* Expand forward if we are between similar content, after whitespace, or at beginning. */
|
||||
BLI_str_cursor_step_utf32(str, str_maxlen, r_end, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,9 @@
|
|||
* - 254..255: invalid.
|
||||
*
|
||||
* Invalid values fall back to 1 byte or -1 (for an error value).
|
||||
*
|
||||
* \note From testing string copying via #BLI_strncpy_utf8 with large (multi-megabyte) strings,
|
||||
* using a function instead of a lookup-table is between 2 & 3 times faster.
|
||||
* \{ */
|
||||
|
||||
BLI_INLINE int utf8_char_compute_skip(const char c)
|
||||
|
@ -310,7 +313,7 @@ int BLI_str_utf8_invalid_strip(char *str, size_t length)
|
|||
BLI_INLINE char *str_utf8_copy_max_bytes_impl(char *dst, const char *src, size_t dst_maxncpy)
|
||||
{
|
||||
/* Cast to `uint8_t` is a no-op, quiets array subscript of type `char` warning.
|
||||
* No need to check `src` points to a nil byte, this will break out of the switch statement. */
|
||||
* No need to check `src` points to a nil byte as this will return from the switch statement. */
|
||||
size_t utf8_size;
|
||||
while ((utf8_size = (size_t)utf8_char_compute_skip(*src)) < dst_maxncpy) {
|
||||
dst_maxncpy -= utf8_size;
|
||||
|
@ -318,12 +321,12 @@ BLI_INLINE char *str_utf8_copy_max_bytes_impl(char *dst, const char *src, size_t
|
|||
/* NOLINTBEGIN: bugprone-assignment-in-if-condition */
|
||||
/* clang-format off */
|
||||
switch (utf8_size) {
|
||||
case 6: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++; ATTR_FALLTHROUGH;
|
||||
case 5: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++; ATTR_FALLTHROUGH;
|
||||
case 4: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++; ATTR_FALLTHROUGH;
|
||||
case 3: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++; ATTR_FALLTHROUGH;
|
||||
case 2: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++; ATTR_FALLTHROUGH;
|
||||
case 1: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++;
|
||||
case 6: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++; ATTR_FALLTHROUGH;
|
||||
case 5: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++; ATTR_FALLTHROUGH;
|
||||
case 4: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++; ATTR_FALLTHROUGH;
|
||||
case 3: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++; ATTR_FALLTHROUGH;
|
||||
case 2: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++; ATTR_FALLTHROUGH;
|
||||
case 1: if (UNLIKELY(!(*dst = *src++))) { return dst; } dst++;
|
||||
}
|
||||
/* clang-format on */
|
||||
/* NOLINTEND: bugprone-assignment-in-if-condition */
|
||||
|
|
|
@ -112,7 +112,7 @@ bool BLI_windows_register_blend_extension(const bool background)
|
|||
&hkey,
|
||||
&dwd);
|
||||
if (lresult == ERROR_SUCCESS) {
|
||||
BLI_snprintf(buffer, sizeof(buffer), "\"%s\" \"%%1\"", BlPath);
|
||||
SNPRINTF(buffer, "\"%s\" \"%%1\"", BlPath);
|
||||
lresult = RegSetValueEx(hkey, NULL, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1);
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ bool BLI_windows_register_blend_extension(const bool background)
|
|||
&hkey,
|
||||
&dwd);
|
||||
if (lresult == ERROR_SUCCESS) {
|
||||
BLI_snprintf(buffer, sizeof(buffer), "\"%s\", 1", BlPath);
|
||||
SNPRINTF(buffer, "\"%s\", 1", BlPath);
|
||||
lresult = RegSetValueEx(hkey, NULL, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1);
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
|
@ -169,12 +169,10 @@ bool BLI_windows_register_blend_extension(const bool background)
|
|||
RegCloseKey(root);
|
||||
printf("success (%s)\n", usr_mode ? "user" : "system");
|
||||
if (!background) {
|
||||
BLI_snprintf(MBox,
|
||||
sizeof(MBox),
|
||||
"File extension registered for %s.",
|
||||
usr_mode ?
|
||||
"the current user. To register for all users, run as an administrator" :
|
||||
"all users");
|
||||
SNPRINTF(MBox,
|
||||
"File extension registered for %s.",
|
||||
usr_mode ? "the current user. To register for all users, run as an administrator" :
|
||||
"all users");
|
||||
MessageBox(0, MBox, "Blender", MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -107,7 +107,7 @@ TEST(listbase, FindLinkOrIndex)
|
|||
TEST(listbase, FindLinkFromStringOrPointer)
|
||||
{
|
||||
struct TestLink {
|
||||
struct TestLink *prev, *next;
|
||||
struct TestLink *next, *prev;
|
||||
char name[64];
|
||||
const void *ptr;
|
||||
};
|
||||
|
|
|
@ -638,6 +638,15 @@ TEST(path_util, Frame)
|
|||
EXPECT_TRUE(ret);
|
||||
EXPECT_STREQ(path, "test_-0100");
|
||||
}
|
||||
|
||||
/* Ensure very large ranges work. */
|
||||
{
|
||||
char path[FILE_MAX * 2];
|
||||
memset(path, '#', sizeof(path));
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
ret = BLI_path_frame(path, sizeof(path), 123456789, 0);
|
||||
EXPECT_TRUE(BLI_str_endswith(path, "0123456789"));
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -987,7 +996,7 @@ TEST(path_util, FrameCheckChars)
|
|||
char path[FILE_MAX]; \
|
||||
bool ret; \
|
||||
STRNCPY(path, input_path); \
|
||||
ret = BLI_path_frame_range(path, sta, end, digits); \
|
||||
ret = BLI_path_frame_range(path, sizeof(path), sta, end, digits); \
|
||||
if (expect_outpath == nullptr) { \
|
||||
EXPECT_FALSE(ret); \
|
||||
} \
|
||||
|
|
|
@ -335,7 +335,7 @@ static void polyfill_to_obj(const char *id,
|
|||
FILE *f;
|
||||
uint i;
|
||||
|
||||
BLI_snprintf(path, sizeof(path), "%s.obj", id);
|
||||
SNPRINTF(path, "%s.obj", id);
|
||||
|
||||
f = fopen(path, "w");
|
||||
if (!f) {
|
||||
|
|
|
@ -122,6 +122,47 @@ TEST(string, StrCopyUTF8_TerminateEncodingEarly)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name String Replace
|
||||
* \{ */
|
||||
|
||||
TEST(string, StrReplaceRange)
|
||||
{
|
||||
#define STR_REPLACE_RANGE(src, size, beg, end, dst, result_expect) \
|
||||
{ \
|
||||
char string[size] = src; \
|
||||
BLI_str_replace_range(string, sizeof(string), beg, end, dst); \
|
||||
EXPECT_STREQ(string, result_expect); \
|
||||
}
|
||||
|
||||
STR_REPLACE_RANGE("a ", 5, 2, 2, "b!", "a b!");
|
||||
STR_REPLACE_RANGE("a ", 4, 2, 2, "b!", "a b");
|
||||
STR_REPLACE_RANGE("a ", 5, 1, 2, "b!", "ab!");
|
||||
STR_REPLACE_RANGE("XYZ", 5, 1, 1, "A", "XAYZ");
|
||||
STR_REPLACE_RANGE("XYZ", 5, 1, 1, "AB", "XABY");
|
||||
STR_REPLACE_RANGE("XYZ", 5, 1, 1, "ABC", "XABC");
|
||||
|
||||
/* Add at the end when there is no room (no-op). */
|
||||
STR_REPLACE_RANGE("XYZA", 5, 4, 4, "?", "XYZA");
|
||||
/* Add at the start, replace all contents. */
|
||||
STR_REPLACE_RANGE("XYZ", 4, 0, 0, "ABC", "ABC");
|
||||
STR_REPLACE_RANGE("XYZ", 7, 0, 0, "ABC", "ABCXYZ");
|
||||
/* Only remove. */
|
||||
STR_REPLACE_RANGE("XYZ", 4, 1, 3, "", "X");
|
||||
STR_REPLACE_RANGE("XYZ", 4, 0, 2, "", "Z");
|
||||
STR_REPLACE_RANGE("XYZ", 4, 0, 3, "", "");
|
||||
/* Only Add. */
|
||||
STR_REPLACE_RANGE("", 4, 0, 0, "XYZ", "XYZ");
|
||||
STR_REPLACE_RANGE("", 4, 0, 0, "XYZ?", "XYZ");
|
||||
/* Do nothing. */
|
||||
STR_REPLACE_RANGE("", 1, 0, 0, "?", "");
|
||||
STR_REPLACE_RANGE("", 1, 0, 0, "", "");
|
||||
|
||||
#undef STR_REPLACE_RANGE
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name String Partition
|
||||
* \{ */
|
||||
|
|
|
@ -239,7 +239,8 @@ static const char *library_parent_filepath(Library *lib)
|
|||
|
||||
struct NewAddress {
|
||||
void *newp;
|
||||
/* `nr` is "user count" for data, and ID code for libdata. */
|
||||
|
||||
/** `nr` is "user count" for data, and ID code for libdata. */
|
||||
int nr;
|
||||
};
|
||||
|
||||
|
@ -261,9 +262,9 @@ static void oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr,
|
|||
onm->map.add_overwrite(oldaddr, NewAddress{newaddr, nr});
|
||||
}
|
||||
|
||||
static void oldnewmap_lib_insert(FileData *fd, const void *oldaddr, ID *newaddr, int nr)
|
||||
static void oldnewmap_lib_insert(FileData *fd, const void *oldaddr, ID *newaddr, int id_code)
|
||||
{
|
||||
oldnewmap_insert(fd->libmap, oldaddr, newaddr, nr);
|
||||
oldnewmap_insert(fd->libmap, oldaddr, newaddr, id_code);
|
||||
}
|
||||
|
||||
void blo_do_versions_oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr)
|
||||
|
@ -1297,8 +1298,8 @@ void blo_filedata_free(FileData *fd)
|
|||
if (fd->libmap && !(fd->flags & FD_FLAGS_NOT_MY_LIBMAP)) {
|
||||
oldnewmap_free(fd->libmap);
|
||||
}
|
||||
if (fd->old_idmap != nullptr) {
|
||||
BKE_main_idmap_destroy(fd->old_idmap);
|
||||
if (fd->old_idmap_uuid != nullptr) {
|
||||
BKE_main_idmap_destroy(fd->old_idmap_uuid);
|
||||
}
|
||||
blo_cache_storage_end(fd);
|
||||
if (fd->bheadmap) {
|
||||
|
@ -1533,10 +1534,10 @@ void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd)
|
|||
|
||||
void blo_make_old_idmap_from_main(FileData *fd, Main *bmain)
|
||||
{
|
||||
if (fd->old_idmap != nullptr) {
|
||||
BKE_main_idmap_destroy(fd->old_idmap);
|
||||
if (fd->old_idmap_uuid != nullptr) {
|
||||
BKE_main_idmap_destroy(fd->old_idmap_uuid);
|
||||
}
|
||||
fd->old_idmap = BKE_main_idmap_create(bmain, false, nullptr, MAIN_IDMAP_TYPE_UUID);
|
||||
fd->old_idmap_uuid = BKE_main_idmap_create(bmain, false, nullptr, MAIN_IDMAP_TYPE_UUID);
|
||||
}
|
||||
|
||||
struct BLOCacheStorage {
|
||||
|
@ -1898,7 +1899,7 @@ static void direct_link_id_override_property_cb(BlendDataReader *reader, void *d
|
|||
}
|
||||
|
||||
static void direct_link_id_common(
|
||||
BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag);
|
||||
BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int id_tag);
|
||||
|
||||
static void direct_link_id_embedded_id(BlendDataReader *reader,
|
||||
Library *current_library,
|
||||
|
@ -1990,7 +1991,7 @@ static int direct_link_id_restore_recalc(const FileData *fd,
|
|||
}
|
||||
|
||||
static void direct_link_id_common(
|
||||
BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag)
|
||||
BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int id_tag)
|
||||
{
|
||||
if (!BLO_read_data_is_undo(reader)) {
|
||||
/* When actually reading a file, we do want to reset/re-generate session UUIDS.
|
||||
|
@ -1998,7 +1999,7 @@ static void direct_link_id_common(
|
|||
id->session_uuid = MAIN_ID_SESSION_UUID_UNSET;
|
||||
}
|
||||
|
||||
if ((tag & LIB_TAG_TEMP_MAIN) == 0) {
|
||||
if ((id_tag & LIB_TAG_TEMP_MAIN) == 0) {
|
||||
BKE_lib_libblock_session_uuid_ensure(id);
|
||||
}
|
||||
|
||||
|
@ -2011,10 +2012,10 @@ static void direct_link_id_common(
|
|||
|
||||
/* Initialize with provided tag. */
|
||||
if (BLO_read_data_is_undo(reader)) {
|
||||
id->tag = tag | (id->tag & LIB_TAG_KEEP_ON_UNDO);
|
||||
id->tag = (id_tag & ~LIB_TAG_KEEP_ON_UNDO) | (id->tag & LIB_TAG_KEEP_ON_UNDO);
|
||||
}
|
||||
else {
|
||||
id->tag = tag;
|
||||
id->tag = id_tag;
|
||||
}
|
||||
|
||||
if (ID_IS_LINKED(id)) {
|
||||
|
@ -2024,7 +2025,7 @@ static void direct_link_id_common(
|
|||
BLO_read_data_address(reader, &id->library_weak_reference);
|
||||
}
|
||||
|
||||
if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) {
|
||||
if (id_tag & LIB_TAG_ID_LINK_PLACEHOLDER) {
|
||||
/* For placeholder we only need to set the tag and properly initialize generic ID fields above,
|
||||
* no further data to read. */
|
||||
return;
|
||||
|
@ -3071,13 +3072,13 @@ static bool read_libblock_undo_restore_linked(FileData *fd, Main *main, const ID
|
|||
|
||||
/* For undo, restore unchanged datablock from old main. */
|
||||
static void read_libblock_undo_restore_identical(
|
||||
FileData *fd, Main *main, const ID * /*id*/, ID *id_old, const int tag)
|
||||
FileData *fd, Main *main, const ID * /*id*/, ID *id_old, const int id_tag)
|
||||
{
|
||||
BLI_assert((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0);
|
||||
BLI_assert(id_old != nullptr);
|
||||
|
||||
/* Some tags need to be preserved here. */
|
||||
id_old->tag = tag | (id_old->tag & LIB_TAG_KEEP_ON_UNDO);
|
||||
id_old->tag = (id_tag & ~LIB_TAG_KEEP_ON_UNDO) | (id_old->tag & LIB_TAG_KEEP_ON_UNDO);
|
||||
id_old->lib = main->curlib;
|
||||
id_old->us = ID_FAKE_USERS(id_old);
|
||||
/* Do not reset id->icon_id here, memory allocated for it remains valid. */
|
||||
|
@ -3146,7 +3147,7 @@ static void read_libblock_undo_restore_at_old_address(FileData *fd, Main *main,
|
|||
}
|
||||
|
||||
static bool read_libblock_undo_restore(
|
||||
FileData *fd, Main *main, BHead *bhead, const int tag, ID **r_id_old)
|
||||
FileData *fd, Main *main, BHead *bhead, int id_tag, ID **r_id_old)
|
||||
{
|
||||
/* Get pointer to memory of new ID that we will be reading. */
|
||||
const ID *id = static_cast<const ID *>(peek_struct_undo(fd, bhead));
|
||||
|
@ -3181,8 +3182,8 @@ static bool read_libblock_undo_restore(
|
|||
|
||||
/* Find the 'current' existing ID we want to reuse instead of the one we
|
||||
* would read from the undo memfile. */
|
||||
BLI_assert(fd->old_idmap != nullptr);
|
||||
id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid);
|
||||
BLI_assert(fd->old_idmap_uuid != nullptr);
|
||||
id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap_uuid, id->session_uuid);
|
||||
}
|
||||
|
||||
if (id_old != nullptr && read_libblock_is_identical(fd, bhead)) {
|
||||
|
@ -3198,7 +3199,7 @@ static bool read_libblock_undo_restore(
|
|||
* it will tell us which ID is re-used from old Main, and which one is actually new. */
|
||||
/* Also do not add LIB_TAG_NEED_LINK, those IDs will never be re-liblinked, hence that tag will
|
||||
* never be cleared, leading to critical issue in link/append code. */
|
||||
const int id_tag = tag | LIB_TAG_UNDO_OLD_ID_REUSED;
|
||||
id_tag |= LIB_TAG_UNDO_OLD_ID_REUSED_UNCHANGED;
|
||||
read_libblock_undo_restore_identical(fd, main, id, id_old, id_tag);
|
||||
|
||||
/* Insert into library map for lookup by newly read datablocks (with pointer value bhead->old).
|
||||
|
@ -3236,7 +3237,7 @@ static bool read_libblock_undo_restore(
|
|||
static BHead *read_libblock(FileData *fd,
|
||||
Main *main,
|
||||
BHead *bhead,
|
||||
const int tag,
|
||||
int id_tag,
|
||||
const bool placeholder_set_indirect_extern,
|
||||
ID **r_id)
|
||||
{
|
||||
|
@ -3257,7 +3258,7 @@ static BHead *read_libblock(FileData *fd,
|
|||
* So undo case does not seem to be affected by this. A future cleanup should try to remove
|
||||
* most of this related code in the future, and instead assert that both `r_id` and
|
||||
* `main->id_map` are `nullptr`. */
|
||||
if (read_libblock_undo_restore(fd, main, bhead, tag, &id_old)) {
|
||||
if (read_libblock_undo_restore(fd, main, bhead, id_tag, &id_old)) {
|
||||
if (r_id) {
|
||||
*r_id = id_old;
|
||||
}
|
||||
|
@ -3307,7 +3308,7 @@ static BHead *read_libblock(FileData *fd,
|
|||
|
||||
/* Set tag for new datablock to indicate lib linking and versioning needs
|
||||
* to be done still. */
|
||||
int id_tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
|
||||
id_tag |= (LIB_TAG_NEED_LINK | LIB_TAG_NEW);
|
||||
|
||||
if (bhead->code == ID_LINK_PLACEHOLDER) {
|
||||
/* Read placeholder for linked datablock. */
|
||||
|
@ -3598,7 +3599,7 @@ static void lib_link_all(FileData *fd, Main *bmain)
|
|||
}
|
||||
|
||||
if ((fd->flags & FD_FLAGS_IS_MEMFILE) && do_partial_undo &&
|
||||
(id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0)
|
||||
(id->tag & LIB_TAG_UNDO_OLD_ID_REUSED_UNCHANGED) != 0)
|
||||
{
|
||||
/* This ID has been re-used from 'old' bmain. Since it was therefore unchanged across
|
||||
* current undo step, and old IDs re-use their old memory address, we do not need to liblink
|
||||
|
@ -4567,7 +4568,7 @@ static void library_link_end(Main *mainl, FileData **fd, const int flag)
|
|||
|
||||
/* Some versioning code does expect some proper userrefcounting, e.g. in conversion from
|
||||
* groups to collections... We could optimize out that first call when we are reading a
|
||||
* current version file, but again this is really not a bottle neck currently. so not worth
|
||||
* current version file, but again this is really not a bottle neck currently. So not worth
|
||||
* it. */
|
||||
BKE_main_id_refcount_recompute(mainvar, false);
|
||||
|
||||
|
|
|
@ -94,7 +94,14 @@ typedef struct FileData {
|
|||
|
||||
struct OldNewMap *datamap;
|
||||
struct OldNewMap *globmap;
|
||||
|
||||
/**
|
||||
* Store mapping from old ID pointers (the values they have in the .blend file) to new ones,
|
||||
* typically from value in `bhead->old` to address in memory where the ID was read.
|
||||
* Used during liblinking process (see #lib_link_all).
|
||||
*/
|
||||
struct OldNewMap *libmap;
|
||||
|
||||
struct OldNewMap *packedmap;
|
||||
struct BLOCacheStorage *cache_storage;
|
||||
|
||||
|
@ -107,7 +114,10 @@ typedef struct FileData {
|
|||
ListBase *mainlist;
|
||||
/** Used for undo. */
|
||||
ListBase *old_mainlist;
|
||||
struct IDNameLib_Map *old_idmap;
|
||||
/**
|
||||
* IDMap using uuids as keys of all the old IDs in the old bmain. Used during undo to find a
|
||||
* matching old data when reading a new ID. */
|
||||
struct IDNameLib_Map *old_idmap_uuid;
|
||||
|
||||
struct BlendFileReadReport *reports;
|
||||
} FileData;
|
||||
|
@ -119,7 +129,7 @@ struct Main;
|
|||
void blo_join_main(ListBase *mainlist);
|
||||
void blo_split_main(ListBase *mainlist, struct Main *main);
|
||||
|
||||
BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath);
|
||||
struct BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath);
|
||||
|
||||
/**
|
||||
* On each new library added, it now checks for the current #FileData and expands relativeness
|
||||
|
|
|
@ -632,7 +632,7 @@ static bool seq_sound_proxy_update_cb(Sequence *seq, void *user_data)
|
|||
Main *bmain = (Main *)user_data;
|
||||
if (seq->type == SEQ_TYPE_SOUND_HD) {
|
||||
char str[FILE_MAX];
|
||||
BLI_path_join(str, sizeof(str), seq->strip->dir, seq->strip->stripdata->name);
|
||||
BLI_path_join(str, sizeof(str), seq->strip->dirpath, seq->strip->stripdata->filename);
|
||||
BLI_path_abs(str, BKE_main_blendfile_path(bmain));
|
||||
seq->sound = BKE_sound_new_file(bmain, str);
|
||||
}
|
||||
|
@ -640,7 +640,7 @@ static bool seq_sound_proxy_update_cb(Sequence *seq, void *user_data)
|
|||
#define SEQ_USE_PROXY_CUSTOM_FILE (1 << 21)
|
||||
/* don't know, if anybody used that this way, but just in case, upgrade to new way... */
|
||||
if ((seq->flag & SEQ_USE_PROXY_CUSTOM_FILE) && !(seq->flag & SEQ_USE_PROXY_CUSTOM_DIR)) {
|
||||
SNPRINTF(seq->strip->proxy->dir, "%s" SEP_STR "BL_proxy", seq->strip->dir);
|
||||
SNPRINTF(seq->strip->proxy->dirpath, "%s" SEP_STR "BL_proxy", seq->strip->dirpath);
|
||||
}
|
||||
#undef SEQ_USE_PROXY_CUSTOM_DIR
|
||||
#undef SEQ_USE_PROXY_CUSTOM_FILE
|
||||
|
|
|
@ -4355,6 +4355,27 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 306, 9)) {
|
||||
/* Fix sound strips with speed factor set to 0. See #107289. */
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
Editing *ed = SEQ_editing_get(scene);
|
||||
if (ed != nullptr) {
|
||||
SEQ_for_each_callback(&ed->seqbase, version_seq_fix_broken_sound_strips, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
|
||||
if (sl->spacetype == SPACE_ACTION) {
|
||||
SpaceAction *saction = reinterpret_cast<SpaceAction *>(sl);
|
||||
saction->cache_display |= TIME_CACHE_SIMULATION_NODES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
|
@ -4366,13 +4387,5 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
|
||||
/* Fix sound strips with speed factor set to 0. See #107289. */
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
Editing *ed = SEQ_editing_get(scene);
|
||||
if (ed != nullptr) {
|
||||
SEQ_for_each_callback(&ed->seqbase, version_seq_fix_broken_sound_strips, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
|
@ -812,7 +813,7 @@ void blo_do_versions_userdef(UserDef *userdef)
|
|||
"Versioning user script path");
|
||||
|
||||
STRNCPY(script_dir->dir_path, userdef->pythondir_legacy);
|
||||
STRNCPY(script_dir->name, DATA_("Untitled"));
|
||||
STRNCPY_UTF8(script_dir->name, DATA_("Untitled"));
|
||||
BLI_addhead(&userdef->script_directories, script_dir);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1063,7 +1063,7 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar)
|
|||
#ifdef WITH_BUILDINFO
|
||||
/* TODO(sergey): Add branch name to file as well? */
|
||||
fg.build_commit_timestamp = build_commit_timestamp;
|
||||
BLI_strncpy(fg.build_hash, build_hash, sizeof(fg.build_hash));
|
||||
STRNCPY(fg.build_hash, build_hash);
|
||||
#else
|
||||
fg.build_commit_timestamp = 0;
|
||||
STRNCPY(fg.build_hash, "unknown");
|
||||
|
|
|
@ -2,13 +2,15 @@
|
|||
* Copyright 2019 Blender Foundation. */
|
||||
#include "blendfile_loading_base_test.h"
|
||||
|
||||
#include "BLI_path_util.h"
|
||||
|
||||
class BlendfileLoadingTest : public BlendfileLoadingBaseTest {
|
||||
};
|
||||
|
||||
TEST_F(BlendfileLoadingTest, CanaryTest)
|
||||
{
|
||||
/* Load the smallest blend file we have in the SVN lib/tests directory. */
|
||||
if (!blendfile_load("modifier_stack/array_test.blend")) {
|
||||
if (!blendfile_load("modifier_stack" SEP_STR "array_test.blend")) {
|
||||
return;
|
||||
}
|
||||
depsgraph_create(DAG_EVAL_RENDER);
|
||||
|
|
|
@ -116,7 +116,7 @@ bool BlendfileLoadingBaseTest::blendfile_load(const char *filepath)
|
|||
return false;
|
||||
}
|
||||
|
||||
char abspath[FILENAME_MAX];
|
||||
char abspath[FILE_MAX];
|
||||
BLI_path_join(abspath, sizeof(abspath), test_assets_dir.c_str(), filepath);
|
||||
|
||||
BlendFileReadReport bf_reports = {nullptr};
|
||||
|
|
|
@ -73,6 +73,7 @@ set(SRC
|
|||
cached_resources/intern/cached_mask.cc
|
||||
cached_resources/intern/cached_texture.cc
|
||||
cached_resources/intern/morphological_distance_feather_weights.cc
|
||||
cached_resources/intern/ocio_color_space_conversion_shader.cc
|
||||
cached_resources/intern/smaa_precomputed_textures.cc
|
||||
cached_resources/intern/symmetric_blur_weights.cc
|
||||
cached_resources/intern/symmetric_separable_blur_weights.cc
|
||||
|
@ -81,6 +82,7 @@ set(SRC
|
|||
cached_resources/COM_cached_resource.hh
|
||||
cached_resources/COM_cached_texture.hh
|
||||
cached_resources/COM_morphological_distance_feather_weights.hh
|
||||
cached_resources/COM_ocio_color_space_conversion_shader.hh
|
||||
cached_resources/COM_smaa_precomputed_textures.hh
|
||||
cached_resources/COM_symmetric_blur_weights.hh
|
||||
cached_resources/COM_symmetric_separable_blur_weights.hh
|
||||
|
@ -173,6 +175,7 @@ set(GLSL_SRC
|
|||
shaders/library/gpu_shader_compositor_main.glsl
|
||||
shaders/library/gpu_shader_compositor_map_value.glsl
|
||||
shaders/library/gpu_shader_compositor_normal.glsl
|
||||
shaders/library/gpu_shader_compositor_ocio_processor.glsl
|
||||
shaders/library/gpu_shader_compositor_posterize.glsl
|
||||
shaders/library/gpu_shader_compositor_separate_combine.glsl
|
||||
shaders/library/gpu_shader_compositor_set_alpha.glsl
|
||||
|
@ -268,4 +271,18 @@ if(WITH_TBB)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_OPENCOLORIO)
|
||||
add_definitions(
|
||||
-DWITH_OCIO
|
||||
)
|
||||
|
||||
list(APPEND INC_SYS
|
||||
${OPENCOLORIO_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
list(APPEND LIB
|
||||
${OPENCOLORIO_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "COM_cached_mask.hh"
|
||||
#include "COM_cached_texture.hh"
|
||||
#include "COM_morphological_distance_feather_weights.hh"
|
||||
#include "COM_ocio_color_space_conversion_shader.hh"
|
||||
#include "COM_smaa_precomputed_textures.hh"
|
||||
#include "COM_symmetric_blur_weights.hh"
|
||||
#include "COM_symmetric_separable_blur_weights.hh"
|
||||
|
@ -41,6 +42,7 @@ class StaticCacheManager {
|
|||
CachedTextureContainer cached_textures;
|
||||
CachedMaskContainer cached_masks;
|
||||
SMAAPrecomputedTexturesContainer smaa_precomputed_textures;
|
||||
OCIOColorSpaceConversionShaderContainer ocio_color_space_conversion_shaders;
|
||||
|
||||
/* Reset the cache manager by deleting the cached resources that are no longer needed because
|
||||
* they weren't used in the last evaluation and prepare the remaining cached resources to track
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "COM_cached_resource.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader Key.
|
||||
*/
|
||||
class OCIOColorSpaceConversionShaderKey {
|
||||
public:
|
||||
std::string source;
|
||||
std::string target;
|
||||
std::string config_cache_id;
|
||||
|
||||
OCIOColorSpaceConversionShaderKey(std::string source,
|
||||
std::string target,
|
||||
std::string config_cache_id);
|
||||
|
||||
uint64_t hash() const;
|
||||
};
|
||||
|
||||
bool operator==(const OCIOColorSpaceConversionShaderKey &a,
|
||||
const OCIOColorSpaceConversionShaderKey &b);
|
||||
|
||||
class GPUShaderCreator;
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader.
|
||||
*
|
||||
* A cached resource that creates and caches a GPU shader that converts the source OCIO color space
|
||||
* of an image into a different target OCIO color space. */
|
||||
class OCIOColorSpaceConversionShader : public CachedResource {
|
||||
private:
|
||||
std::shared_ptr<GPUShaderCreator> shader_creator_;
|
||||
|
||||
public:
|
||||
OCIOColorSpaceConversionShader(std::string source, std::string target);
|
||||
|
||||
GPUShader *bind_shader_and_resources();
|
||||
|
||||
void unbind_shader_and_resources();
|
||||
|
||||
const char *input_sampler_name();
|
||||
|
||||
const char *output_image_name();
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader Container.
|
||||
*/
|
||||
class OCIOColorSpaceConversionShaderContainer : CachedResourceContainer {
|
||||
private:
|
||||
Map<OCIOColorSpaceConversionShaderKey, std::unique_ptr<OCIOColorSpaceConversionShader>> map_;
|
||||
|
||||
public:
|
||||
void reset() override;
|
||||
|
||||
/* Check if there is an available OCIOColorSpaceConversionShader cached resource with the given
|
||||
* parameters in the container, if one exists, return it, otherwise, return a newly created one
|
||||
* and add it to the container. In both cases, tag the cached resource as needed to keep it
|
||||
* cached for the next evaluation. */
|
||||
OCIOColorSpaceConversionShader &get(std::string source, std::string target);
|
||||
};
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -0,0 +1,502 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
#include "GPU_uniform_buffer.h"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
#include "COM_ocio_color_space_conversion_shader.hh"
|
||||
|
||||
#if defined(WITH_OCIO)
|
||||
# include <OpenColorIO/OpenColorIO.h>
|
||||
#endif
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader Key.
|
||||
*/
|
||||
|
||||
OCIOColorSpaceConversionShaderKey::OCIOColorSpaceConversionShaderKey(std::string source,
|
||||
std::string target,
|
||||
std::string config_cache_id)
|
||||
: source(source), target(target), config_cache_id(config_cache_id)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t OCIOColorSpaceConversionShaderKey::hash() const
|
||||
{
|
||||
return get_default_hash_3(source, target, config_cache_id);
|
||||
}
|
||||
|
||||
bool operator==(const OCIOColorSpaceConversionShaderKey &a,
|
||||
const OCIOColorSpaceConversionShaderKey &b)
|
||||
{
|
||||
return a.source == b.source && a.target == b.target && a.config_cache_id == b.config_cache_id;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* GPU Shader Creator.
|
||||
*/
|
||||
|
||||
#if defined(WITH_OCIO)
|
||||
|
||||
namespace OCIO = OCIO_NAMESPACE;
|
||||
using namespace blender::gpu::shader;
|
||||
|
||||
/* A subclass of OCIO::GpuShaderCreator that constructs the shader using a ShaderCreateInfo. The
|
||||
* Create method should be used to construct the creator, then the extractGpuShaderInfo() method of
|
||||
* the appropriate OCIO::GPUProcessor should be called passing in the creator. After construction,
|
||||
* the constructed compute shader can be used by calling the bind_shader_and_resources() method,
|
||||
* followed by binding the input texture and output image using their names input_sampler_name()
|
||||
* and output_image_name(), following by dispatching the shader on the domain of the input, and
|
||||
* finally calling the unbind_shader_and_resources() method.
|
||||
*
|
||||
* Upon calling the extractGpuShaderInfo(), all the transforms in the GPU processor will add their
|
||||
* needed resources by calling the respective addUniform() and add[3D]Texture() methods. Then, the
|
||||
* shader code of all transforms will be generated and passed to the createShaderText() method,
|
||||
* generating the full code of the processor. Finally, the finalize() method will be called to
|
||||
* finally create the shader. */
|
||||
class GPUShaderCreator : public OCIO::GpuShaderCreator {
|
||||
public:
|
||||
static std::shared_ptr<GPUShaderCreator> Create()
|
||||
{
|
||||
std::shared_ptr<GPUShaderCreator> instance = std::make_shared<GPUShaderCreator>();
|
||||
instance->setLanguage(OCIO::GPU_LANGUAGE_GLSL_4_0);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/* Not used, but needs to be overridden, so return a nullptr. */
|
||||
OCIO::GpuShaderCreatorRcPtr clone() const override
|
||||
{
|
||||
return OCIO::GpuShaderCreatorRcPtr();
|
||||
}
|
||||
|
||||
/* This is ignored since we query using our own GPU capabilities system. */
|
||||
void setTextureMaxWidth(unsigned max_width) override {}
|
||||
|
||||
unsigned getTextureMaxWidth() const noexcept override
|
||||
{
|
||||
return GPU_max_texture_size();
|
||||
}
|
||||
|
||||
bool addUniform(const char *name, const DoubleGetter &get_double) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case, returning false
|
||||
* indicates failure to add the uniform for the shader creator. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(name))) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.push_constant(Type::FLOAT, resource_name);
|
||||
|
||||
float_uniforms_.add(resource_name, get_double);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addUniform(const char *name, const BoolGetter &get_bool) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case, returning false
|
||||
* indicates failure to add the uniform for the shader creator. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(name))) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.push_constant(Type::BOOL, resource_name);
|
||||
|
||||
boolean_uniforms_.add(name, get_bool);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addUniform(const char *name, const Float3Getter &get_float3) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case, returning false
|
||||
* indicates failure to add the uniform for the shader creator. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(name))) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.push_constant(Type::VEC3, resource_name);
|
||||
|
||||
vector_uniforms_.add(resource_name, get_float3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addUniform(const char *name,
|
||||
const SizeGetter &get_size,
|
||||
const VectorFloatGetter &get_vector_float) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case, returning false
|
||||
* indicates failure to add the uniform for the shader creator. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(name))) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.uniform_buf(buffers_sizes_.size(), "float", resource_name);
|
||||
|
||||
float_buffers_.add(resource_name, get_vector_float);
|
||||
buffers_sizes_.add(resource_name, get_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addUniform(const char *name,
|
||||
const SizeGetter &get_size,
|
||||
const VectorIntGetter &get_vector_int) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case, returning false
|
||||
* indicates failure to add the uniform for the shader creator. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(name))) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.uniform_buf(buffers_sizes_.size(), "int", resource_name);
|
||||
|
||||
int_buffers_.add(name, get_vector_int);
|
||||
buffers_sizes_.add(name, get_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void addTexture(const char *texture_name,
|
||||
const char *sampler_name,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
TextureType channel,
|
||||
OCIO::Interpolation interpolation,
|
||||
const float *values) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(sampler_name))) {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
|
||||
GPUTexture *texture;
|
||||
eGPUTextureFormat texture_format = (channel == TEXTURE_RGB_CHANNEL) ? GPU_RGB16F : GPU_R16F;
|
||||
/* A height of 1 indicates a 1D texture according to the OCIO API. */
|
||||
if (height == 1) {
|
||||
texture = GPU_texture_create_1d(
|
||||
texture_name, width, 1, texture_format, GPU_TEXTURE_USAGE_SHADER_READ, values);
|
||||
shader_create_info_.sampler(textures_.size() + 1, ImageType::FLOAT_1D, resource_name);
|
||||
}
|
||||
else {
|
||||
texture = GPU_texture_create_2d(
|
||||
texture_name, width, height, 1, texture_format, GPU_TEXTURE_USAGE_SHADER_READ, values);
|
||||
shader_create_info_.sampler(textures_.size() + 1, ImageType::FLOAT_2D, resource_name);
|
||||
}
|
||||
GPU_texture_filter_mode(texture, interpolation != OCIO::INTERP_NEAREST);
|
||||
|
||||
textures_.add(sampler_name, texture);
|
||||
}
|
||||
|
||||
void add3DTexture(const char *texture_name,
|
||||
const char *sampler_name,
|
||||
unsigned size,
|
||||
OCIO::Interpolation interpolation,
|
||||
const float *values) override
|
||||
{
|
||||
/* Check if a resource exists with the same name and assert if it is the case. */
|
||||
if (!resource_names_.add(std::make_unique<std::string>(sampler_name))) {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/* Don't use the name argument directly since ShaderCreateInfo only stores references to
|
||||
* resource names, instead, use the name that is stored in resource_names_. */
|
||||
std::string &resource_name = *resource_names_[resource_names_.size() - 1];
|
||||
shader_create_info_.sampler(textures_.size() + 1, ImageType::FLOAT_3D, resource_name);
|
||||
|
||||
GPUTexture *texture = GPU_texture_create_3d(
|
||||
texture_name, size, size, size, 1, GPU_RGB16F, GPU_TEXTURE_USAGE_SHADER_READ, values);
|
||||
GPU_texture_filter_mode(texture, interpolation != OCIO::INTERP_NEAREST);
|
||||
|
||||
textures_.add(sampler_name, texture);
|
||||
}
|
||||
|
||||
/* This gets called before the finalize() method to construct the shader code. We just
|
||||
* concatenate the code except for the declarations section. That's because the ShaderCreateInfo
|
||||
* will add the declaration itself. */
|
||||
void createShaderText(const char *declarations,
|
||||
const char *helper_methods,
|
||||
const char *function_header,
|
||||
const char *function_body,
|
||||
const char *function_footer) override
|
||||
{
|
||||
shader_code_ += helper_methods;
|
||||
shader_code_ += function_header;
|
||||
shader_code_ += function_body;
|
||||
shader_code_ += function_footer;
|
||||
}
|
||||
|
||||
/* This gets called when all resources were added using the respective addUniform() or
|
||||
* add[3D]Texture() methods and the shader code was generated using the createShaderText()
|
||||
* method. That is, we are ready to complete the ShaderCreateInfo and create a shader from it. */
|
||||
void finalize() override
|
||||
{
|
||||
GpuShaderCreator::finalize();
|
||||
|
||||
shader_create_info_.local_group_size(16, 16);
|
||||
shader_create_info_.sampler(0, ImageType::FLOAT_2D, input_sampler_name());
|
||||
shader_create_info_.image(
|
||||
0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, output_image_name());
|
||||
shader_create_info_.compute_source("gpu_shader_compositor_ocio_processor.glsl");
|
||||
shader_create_info_.compute_source_generated += shader_code_;
|
||||
|
||||
GPUShaderCreateInfo *info = reinterpret_cast<GPUShaderCreateInfo *>(&shader_create_info_);
|
||||
shader_ = GPU_shader_create_from_info(info);
|
||||
}
|
||||
|
||||
GPUShader *bind_shader_and_resources()
|
||||
{
|
||||
GPU_shader_bind(shader_);
|
||||
|
||||
for (auto item : float_uniforms_.items()) {
|
||||
GPU_shader_uniform_1f(shader_, item.key.c_str(), item.value());
|
||||
}
|
||||
|
||||
for (auto item : boolean_uniforms_.items()) {
|
||||
GPU_shader_uniform_1b(shader_, item.key.c_str(), item.value());
|
||||
}
|
||||
|
||||
for (auto item : vector_uniforms_.items()) {
|
||||
GPU_shader_uniform_3fv(shader_, item.key.c_str(), item.value().data());
|
||||
}
|
||||
|
||||
for (auto item : float_buffers_.items()) {
|
||||
GPUUniformBuf *buffer = GPU_uniformbuf_create_ex(
|
||||
buffers_sizes_.lookup(item.key)(), item.value(), item.key.c_str());
|
||||
const int ubo_location = GPU_shader_get_ubo_binding(shader_, item.key.c_str());
|
||||
GPU_uniformbuf_bind(buffer, ubo_location);
|
||||
uniform_buffers_.append(buffer);
|
||||
}
|
||||
|
||||
for (auto item : int_buffers_.items()) {
|
||||
GPUUniformBuf *buffer = GPU_uniformbuf_create_ex(
|
||||
buffers_sizes_.lookup(item.key)(), item.value(), item.key.c_str());
|
||||
const int ubo_location = GPU_shader_get_ubo_binding(shader_, item.key.c_str());
|
||||
GPU_uniformbuf_bind(buffer, ubo_location);
|
||||
uniform_buffers_.append(buffer);
|
||||
}
|
||||
|
||||
for (auto item : textures_.items()) {
|
||||
const int texture_image_unit = GPU_shader_get_sampler_binding(shader_, item.key.c_str());
|
||||
GPU_texture_bind(item.value, texture_image_unit);
|
||||
}
|
||||
|
||||
return shader_;
|
||||
}
|
||||
|
||||
void unbind_shader_and_resources()
|
||||
{
|
||||
for (GPUUniformBuf *buffer : uniform_buffers_) {
|
||||
GPU_uniformbuf_unbind(buffer);
|
||||
GPU_uniformbuf_free(buffer);
|
||||
}
|
||||
|
||||
for (GPUTexture *texture : textures_.values()) {
|
||||
GPU_texture_unbind(texture);
|
||||
}
|
||||
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
const char *input_sampler_name()
|
||||
{
|
||||
return "input_tx";
|
||||
}
|
||||
|
||||
const char *output_image_name()
|
||||
{
|
||||
return "output_img";
|
||||
}
|
||||
|
||||
~GPUShaderCreator() override
|
||||
{
|
||||
for (GPUTexture *texture : textures_.values()) {
|
||||
GPU_texture_free(texture);
|
||||
}
|
||||
|
||||
GPU_shader_free(shader_);
|
||||
}
|
||||
|
||||
private:
|
||||
/* The processor shader and the ShaderCreateInfo used to construct it. Constructed and
|
||||
* initialized in the finalize() method. */
|
||||
GPUShader *shader_ = nullptr;
|
||||
ShaderCreateInfo shader_create_info_ = ShaderCreateInfo("OCIO Processor");
|
||||
|
||||
/* Stores the generated OCIOMain function as well as a number of helper functions. Initialized in
|
||||
* the createShaderText() method. */
|
||||
std::string shader_code_;
|
||||
|
||||
/* Maps that associates the name of a uniform with a getter function that returns its value.
|
||||
* Initialized in the respective addUniform() methods. */
|
||||
Map<std::string, DoubleGetter> float_uniforms_;
|
||||
Map<std::string, BoolGetter> boolean_uniforms_;
|
||||
Map<std::string, Float3Getter> vector_uniforms_;
|
||||
|
||||
/* Maps that associates the name of uniform buffer objects with a getter function that returns
|
||||
* its values. Initialized in the respective addUniform() methods. */
|
||||
Map<std::string, VectorFloatGetter> float_buffers_;
|
||||
Map<std::string, VectorIntGetter> int_buffers_;
|
||||
|
||||
/* A map that associates the name of uniform buffer objects with a getter functions that returns
|
||||
* its number of elements. Initialized in the respective addUniform() methods. */
|
||||
Map<std::string, SizeGetter> buffers_sizes_;
|
||||
|
||||
/* A map that associates the name of a sampler with its corresponding texture. Initialized in the
|
||||
* addTexture() and add3DTexture() methods. */
|
||||
Map<std::string, GPUTexture *> textures_;
|
||||
|
||||
/* A vector set that stores the names of all the resources used by the shader. This is used to:
|
||||
* 1. Check for name collisions when adding new resources.
|
||||
* 2. Store the resource names throughout the construction of the shader since the
|
||||
* ShaderCreateInfo class only stores references to resources names. */
|
||||
VectorSet<std::unique_ptr<std::string>> resource_names_;
|
||||
|
||||
/* A vectors that stores the created uniform buffers when bind_shader_and_resources() is called,
|
||||
* so that they can be properly unbound and freed in the unbind_shader_and_resources() method. */
|
||||
Vector<GPUUniformBuf *> uniform_buffers_;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
/* A stub implementation in case OCIO is disabled at build time. */
|
||||
class GPUShaderCreator {
|
||||
public:
|
||||
GPUShader *bind_shader_and_resources()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void unbind_shader_and_resources() {}
|
||||
|
||||
const char *input_sampler_name()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *output_image_name()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader.
|
||||
*/
|
||||
|
||||
OCIOColorSpaceConversionShader::OCIOColorSpaceConversionShader(std::string source,
|
||||
std::string target)
|
||||
{
|
||||
#if defined(WITH_OCIO)
|
||||
/* Get a GPU processor that transforms the source color space to the target color space. */
|
||||
OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
|
||||
OCIO::ConstProcessorRcPtr processor = config->getProcessor(source.c_str(), target.c_str());
|
||||
OCIO::ConstGPUProcessorRcPtr gpu_processor = processor->getDefaultGPUProcessor();
|
||||
|
||||
/* Create a GPU shader creator and construct it based on the transforms in the default GPU
|
||||
* processor. */
|
||||
shader_creator_ = GPUShaderCreator::Create();
|
||||
auto ocio_shader_creator = std::static_pointer_cast<OCIO::GpuShaderCreator>(shader_creator_);
|
||||
gpu_processor->extractGpuShaderInfo(ocio_shader_creator);
|
||||
#endif
|
||||
}
|
||||
|
||||
GPUShader *OCIOColorSpaceConversionShader::bind_shader_and_resources()
|
||||
{
|
||||
return shader_creator_->bind_shader_and_resources();
|
||||
}
|
||||
|
||||
void OCIOColorSpaceConversionShader::unbind_shader_and_resources()
|
||||
{
|
||||
shader_creator_->unbind_shader_and_resources();
|
||||
}
|
||||
|
||||
const char *OCIOColorSpaceConversionShader::input_sampler_name()
|
||||
{
|
||||
return shader_creator_->input_sampler_name();
|
||||
}
|
||||
|
||||
const char *OCIOColorSpaceConversionShader::output_image_name()
|
||||
{
|
||||
return shader_creator_->output_image_name();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* OCIO Color Space Conversion Shader Container.
|
||||
*/
|
||||
|
||||
void OCIOColorSpaceConversionShaderContainer::reset()
|
||||
{
|
||||
/* First, delete all resources that are no longer needed. */
|
||||
map_.remove_if([](auto item) { return !item.value->needed; });
|
||||
|
||||
/* Second, reset the needed status of the remaining resources to false to ready them to track
|
||||
* their needed status for the next evaluation. */
|
||||
for (auto &value : map_.values()) {
|
||||
value->needed = false;
|
||||
}
|
||||
}
|
||||
|
||||
OCIOColorSpaceConversionShader &OCIOColorSpaceConversionShaderContainer::get(std::string source,
|
||||
std::string target)
|
||||
{
|
||||
#if defined(WITH_OCIO)
|
||||
/* Use the config cache ID in the cache key in case the configuration changed at runtime. */
|
||||
std::string config_cache_id = OCIO::GetCurrentConfig()->getCacheID();
|
||||
#else
|
||||
std::string config_cache_id;
|
||||
#endif
|
||||
|
||||
const OCIOColorSpaceConversionShaderKey key(source, target, config_cache_id);
|
||||
|
||||
OCIOColorSpaceConversionShader &shader = *map_.lookup_or_add_cb(
|
||||
key, [&]() { return std::make_unique<OCIOColorSpaceConversionShader>(source, target); });
|
||||
|
||||
shader.needed = true;
|
||||
return shader;
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -12,6 +12,7 @@ void StaticCacheManager::reset()
|
|||
cached_textures.reset();
|
||||
cached_masks.reset();
|
||||
smaa_precomputed_textures.reset();
|
||||
ocio_color_space_conversion_shaders.reset();
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
|
||||
|
||||
/* OCIOMain will be dynamically generated in the OCIOColorSpaceConversionShader class and appended
|
||||
* at the end of this file, so forward declare it. Such forward declarations are not supported nor
|
||||
* needed on Metal. */
|
||||
#if !defined(GPU_METAL)
|
||||
vec4 OCIOMain(vec4 inPixel);
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
imageStore(output_img, texel, OCIOMain(texture_load(input_tx, texel)));
|
||||
}
|
|
@ -139,7 +139,7 @@ static void extract_tan_init_common(const MeshRenderData *mr,
|
|||
char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
|
||||
const char *layer_name = CustomData_get_layer_name(r_loop_data, CD_TANGENT, 0);
|
||||
GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
|
||||
BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name);
|
||||
SNPRINTF(attr_name, "t%s", attr_safe_name);
|
||||
GPU_vertformat_attr_add(format, attr_name, comp_type, 4, fetch_mode);
|
||||
GPU_vertformat_alias_add(format, "t");
|
||||
GPU_vertformat_alias_add(format, "at");
|
||||
|
|
|
@ -368,7 +368,7 @@ static void acf_generic_idblock_name(bAnimListElem *ale, char *name)
|
|||
|
||||
/* just copy the name... */
|
||||
if (id && name) {
|
||||
BLI_strncpy(name, id->name + 2, ANIM_CHAN_NAME_SIZE);
|
||||
BLI_strncpy_utf8(name, id->name + 2, ANIM_CHAN_NAME_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -476,7 +476,7 @@ static void acf_summary_backdrop(bAnimContext *ac, bAnimListElem *ale, float ymi
|
|||
static void acf_summary_name(bAnimListElem *UNUSED(ale), char *name)
|
||||
{
|
||||
if (name) {
|
||||
BLI_strncpy(name, IFACE_("Summary"), ANIM_CHAN_NAME_SIZE);
|
||||
BLI_strncpy_utf8(name, IFACE_("Summary"), ANIM_CHAN_NAME_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1178,7 +1178,7 @@ static void acf_nla_controls_backdrop(bAnimContext *ac,
|
|||
/* name for nla controls expander entries */
|
||||
static void acf_nla_controls_name(bAnimListElem *UNUSED(ale), char *name)
|
||||
{
|
||||
BLI_strncpy(name, IFACE_("NLA Strip Controls"), ANIM_CHAN_NAME_SIZE);
|
||||
BLI_strncpy_utf8(name, IFACE_("NLA Strip Controls"), ANIM_CHAN_NAME_SIZE);
|
||||
}
|
||||
|
||||
/* check if some setting exists for this channel */
|
||||
|
@ -1393,7 +1393,7 @@ static int acf_filldrivers_icon(bAnimListElem *UNUSED(ale))
|
|||
|
||||
static void acf_filldrivers_name(bAnimListElem *UNUSED(ale), char *name)
|
||||
{
|
||||
BLI_strncpy(name, IFACE_("Drivers"), ANIM_CHAN_NAME_SIZE);
|
||||
BLI_strncpy_utf8(name, IFACE_("Drivers"), ANIM_CHAN_NAME_SIZE);
|
||||
}
|
||||
|
||||
/* check if some setting exists for this channel */
|
||||
|
@ -4468,6 +4468,10 @@ void ANIM_channel_draw(
|
|||
/* just skip - drawn as widget now */
|
||||
offset += ICON_WIDTH;
|
||||
}
|
||||
else {
|
||||
/* A bit of padding when there is no expand widget. */
|
||||
offset += (short)(0.2f * U.widget_unit);
|
||||
}
|
||||
|
||||
/* step 3) draw icon ............................................... */
|
||||
if (acf->icon) {
|
||||
|
@ -4522,10 +4526,6 @@ void ANIM_channel_draw(
|
|||
offset += ICON_WIDTH;
|
||||
}
|
||||
}
|
||||
else if ((ac->spacetype == SPACE_NLA) && acf->has_setting(ac, ale, ACHANNEL_SETTING_SOLO)) {
|
||||
/* just skip - drawn as widget now */
|
||||
offset += ICON_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
/* step 5) draw name ............................................... */
|
||||
|
@ -5252,11 +5252,6 @@ void ANIM_channel_draw_widgets(const bContext *C,
|
|||
offset += ICON_WIDTH;
|
||||
}
|
||||
}
|
||||
else if ((ac->spacetype == SPACE_NLA) && acf->has_setting(ac, ale, ACHANNEL_SETTING_SOLO)) {
|
||||
/* 'solo' setting for NLA Tracks */
|
||||
draw_setting_widget(ac, ale, acf, block, offset, ymid, ACHANNEL_SETTING_SOLO);
|
||||
offset += ICON_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
/* step 4) draw text - check if renaming widget is in use... */
|
||||
|
@ -5338,6 +5333,13 @@ void ANIM_channel_draw_widgets(const bContext *C,
|
|||
/* check if there's enough space for the toggles if the sliders are drawn too */
|
||||
if (!(draw_sliders) || (BLI_rcti_size_x(&v2d->mask) > ANIM_UI_get_channel_button_width() / 2))
|
||||
{
|
||||
/* solo... */
|
||||
if ((ac->spacetype == SPACE_NLA) && acf->has_setting(ac, ale, ACHANNEL_SETTING_SOLO)) {
|
||||
offset -= ICON_WIDTH;
|
||||
draw_setting_widget(ac, ale, acf, block, offset, ymid, ACHANNEL_SETTING_SOLO);
|
||||
/* A touch of padding because the star icon is so wide. */
|
||||
offset -= (short)(0.2f * ICON_WIDTH);
|
||||
}
|
||||
/* protect... */
|
||||
if (acf->has_setting(ac, ale, ACHANNEL_SETTING_PROTECT)) {
|
||||
offset -= ICON_WIDTH;
|
||||
|
|
|
@ -1920,9 +1920,19 @@ void FONT_OT_selection_set(struct wmOperatorType *ot)
|
|||
|
||||
static int font_select_word_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
move_cursor(C, NEXT_CHAR, false);
|
||||
move_cursor(C, PREV_WORD, false);
|
||||
move_cursor(C, NEXT_WORD, true);
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
Curve *cu = obedit->data;
|
||||
EditFont *ef = cu->editfont;
|
||||
|
||||
BLI_str_cursor_step_bounds_utf32(ef->textbuf, ef->len, ef->pos, &ef->selstart, &ef->selend);
|
||||
ef->pos = ef->selend;
|
||||
|
||||
/* XXX: Text object selection start is 1-based, unlike text processing elsewhere in Blender. */
|
||||
ef->selstart += 1;
|
||||
|
||||
font_select_update_primary_clipboard(obedit);
|
||||
text_update_edited(C, obedit, FO_CURS);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
|
|
|
@ -3388,7 +3388,7 @@ void ED_gpencil_layer_merge(bGPdata *gpd,
|
|||
}
|
||||
}
|
||||
|
||||
static void gpencil_layer_new_name_get(bGPdata *gpd, char *rname)
|
||||
static void gpencil_layer_new_name_get(bGPdata *gpd, char *r_name, size_t name_maxncpy)
|
||||
{
|
||||
int index = 0;
|
||||
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
||||
|
@ -3398,12 +3398,10 @@ static void gpencil_layer_new_name_get(bGPdata *gpd, char *rname)
|
|||
}
|
||||
|
||||
if (index == 0) {
|
||||
BLI_strncpy(rname, "GP_Layer", 128);
|
||||
BLI_strncpy(r_name, "GP_Layer", name_maxncpy);
|
||||
return;
|
||||
}
|
||||
char *name = BLI_sprintfN("%.*s.%03d", 128, "GP_Layer", index);
|
||||
BLI_strncpy(rname, name, 128);
|
||||
MEM_freeN(name);
|
||||
BLI_snprintf(r_name, name_maxncpy, "GP_Layer.%03d", index);
|
||||
}
|
||||
|
||||
int ED_gpencil_new_layer_dialog(bContext *C, wmOperator *op)
|
||||
|
@ -3415,7 +3413,7 @@ int ED_gpencil_new_layer_dialog(bContext *C, wmOperator *op)
|
|||
if (!RNA_property_is_set(op->ptr, prop)) {
|
||||
char name[MAX_NAME];
|
||||
bGPdata *gpd = ob->data;
|
||||
gpencil_layer_new_name_get(gpd, name);
|
||||
gpencil_layer_new_name_get(gpd, name, sizeof(name));
|
||||
RNA_property_string_set(op->ptr, prop, name);
|
||||
return WM_operator_props_dialog_popup(C, op, 200);
|
||||
}
|
||||
|
|
|
@ -3937,11 +3937,11 @@ static void ui_but_update_ex(uiBut *but, const bool validate)
|
|||
(void)str; /* UNUSED */
|
||||
}
|
||||
else {
|
||||
STRNCPY(but->drawstr, IFACE_("Press a key"));
|
||||
STRNCPY_UTF8(but->drawstr, IFACE_("Press a key"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
STRNCPY(but->drawstr, but->str);
|
||||
STRNCPY_UTF8(but->drawstr, but->str);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -3730,8 +3730,11 @@ static void ui_do_but_textedit(
|
|||
|
||||
/* only select a word in button if there was no selection before */
|
||||
if (event->val == KM_DBL_CLICK && had_selection == false) {
|
||||
ui_textedit_move(but, data, STRCUR_DIR_PREV, false, STRCUR_JUMP_DELIM);
|
||||
ui_textedit_move(but, data, STRCUR_DIR_NEXT, true, STRCUR_JUMP_DELIM);
|
||||
int selsta, selend;
|
||||
BLI_str_cursor_step_bounds_utf8(data->str, strlen(data->str), but->pos, &selsta, &selend);
|
||||
but->pos = (short)selend;
|
||||
but->selsta = (short)selsta;
|
||||
but->selend = (short)selend;
|
||||
retval = WM_UI_HANDLER_BREAK;
|
||||
changed = true;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
|
@ -2021,7 +2022,7 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op)
|
|||
BKE_nlatrack_add_strip(nlt, strip, is_liboverride);
|
||||
|
||||
/* Auto-name the strip, and give the track an interesting name. */
|
||||
STRNCPY(nlt->name, DATA_("SoundTrack"));
|
||||
STRNCPY_UTF8(nlt->name, DATA_("SoundTrack"));
|
||||
BKE_nlastrip_validate_name(adt, strip);
|
||||
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, nullptr);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue