Alternative Upload geometry data in parallel to multiple GPUs using the "Multi-Device" #107552
18
GNUmakefile
18
GNUmakefile
|
@ -58,9 +58,6 @@ Static Source Code Checking
|
|||
* check_cppcheck: Run blender source through cppcheck (C & C++).
|
||||
* check_clang_array: Run blender source through clang array checking script (C & C++).
|
||||
* check_deprecated: Check if there is any deprecated code to remove.
|
||||
* check_splint: Run blenders source through splint (C only).
|
||||
* check_sparse: Run blenders source through sparse (C only).
|
||||
* check_smatch: Run blenders source through smatch (C only).
|
||||
* check_descriptions: Check for duplicate/invalid descriptions.
|
||||
* check_licenses: Check license headers follow the SPDX license specification,
|
||||
using one of the accepted licenses in 'doc/license/SPDX-license-identifiers.txt'
|
||||
|
@ -474,21 +471,6 @@ check_clang_array: .FORCE
|
|||
@cd "$(BUILD_DIR)" ; \
|
||||
$(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_clang_array.py"
|
||||
|
||||
check_splint: .FORCE
|
||||
@$(CMAKE_CONFIG)
|
||||
@cd "$(BUILD_DIR)" ; \
|
||||
$(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_splint.py"
|
||||
|
||||
check_sparse: .FORCE
|
||||
@$(CMAKE_CONFIG)
|
||||
@cd "$(BUILD_DIR)" ; \
|
||||
$(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_sparse.py"
|
||||
|
||||
check_smatch: .FORCE
|
||||
@$(CMAKE_CONFIG)
|
||||
@cd "$(BUILD_DIR)" ; \
|
||||
$(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_smatch.py"
|
||||
|
||||
check_mypy: .FORCE
|
||||
@$(PYTHON) "$(BLENDER_DIR)/tools/check_source/check_mypy.py"
|
||||
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
CHECKER_IGNORE_PREFIX = [
|
||||
"extern",
|
||||
"intern/moto",
|
||||
]
|
||||
|
||||
CHECKER_BIN = "smatch"
|
||||
CHECKER_ARGS = [
|
||||
"--full-path",
|
||||
"--two-passes",
|
||||
]
|
||||
|
||||
import project_source_info
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
USE_QUIET = (os.environ.get("QUIET", None) is not None)
|
||||
|
||||
|
||||
def main():
|
||||
source_info = project_source_info.build_info(use_cxx=False, ignore_prefix_list=CHECKER_IGNORE_PREFIX)
|
||||
source_defines = project_source_info.build_defines_as_args()
|
||||
|
||||
check_commands = []
|
||||
for c, inc_dirs, defs in source_info:
|
||||
|
||||
cmd = ([CHECKER_BIN] +
|
||||
CHECKER_ARGS +
|
||||
[c] +
|
||||
[("-I%s" % i) for i in inc_dirs] +
|
||||
[("-D%s" % d) for d in defs] +
|
||||
source_defines
|
||||
)
|
||||
|
||||
check_commands.append((c, cmd))
|
||||
|
||||
def my_process(i, c, cmd):
|
||||
if not USE_QUIET:
|
||||
percent = 100.0 * (i / len(check_commands))
|
||||
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stdout.write("%s %s\n" % (percent_str, c))
|
||||
|
||||
return subprocess.Popen(cmd)
|
||||
|
||||
process_functions = []
|
||||
for i, (c, cmd) in enumerate(check_commands):
|
||||
process_functions.append((my_process, (i, c, cmd)))
|
||||
|
||||
project_source_info.queue_processes(process_functions)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,56 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
CHECKER_IGNORE_PREFIX = [
|
||||
"extern",
|
||||
"intern/moto",
|
||||
]
|
||||
|
||||
CHECKER_BIN = "sparse"
|
||||
CHECKER_ARGS = [
|
||||
]
|
||||
|
||||
import project_source_info
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
USE_QUIET = (os.environ.get("QUIET", None) is not None)
|
||||
|
||||
|
||||
def main():
|
||||
source_info = project_source_info.build_info(use_cxx=False, ignore_prefix_list=CHECKER_IGNORE_PREFIX)
|
||||
source_defines = project_source_info.build_defines_as_args()
|
||||
|
||||
check_commands = []
|
||||
for c, inc_dirs, defs in source_info:
|
||||
|
||||
cmd = ([CHECKER_BIN] +
|
||||
CHECKER_ARGS +
|
||||
[c] +
|
||||
[("-I%s" % i) for i in inc_dirs] +
|
||||
[("-D%s" % d) for d in defs] +
|
||||
source_defines
|
||||
)
|
||||
|
||||
check_commands.append((c, cmd))
|
||||
|
||||
def my_process(i, c, cmd):
|
||||
if not USE_QUIET:
|
||||
percent = 100.0 * (i / len(check_commands))
|
||||
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stdout.write("%s %s\n" % (percent_str, c))
|
||||
|
||||
return subprocess.Popen(cmd)
|
||||
|
||||
process_functions = []
|
||||
for i, (c, cmd) in enumerate(check_commands):
|
||||
process_functions.append((my_process, (i, c, cmd)))
|
||||
|
||||
project_source_info.queue_processes(process_functions)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,86 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
CHECKER_IGNORE_PREFIX = [
|
||||
"extern",
|
||||
"intern/moto",
|
||||
]
|
||||
|
||||
CHECKER_BIN = "splint"
|
||||
|
||||
CHECKER_ARGS = [
|
||||
"-weak",
|
||||
"-posix-lib",
|
||||
"-linelen", "10000",
|
||||
"+ignorequals",
|
||||
"+relaxtypes",
|
||||
"-retvalother",
|
||||
"+matchanyintegral",
|
||||
"+longintegral",
|
||||
"+ignoresigns",
|
||||
"-nestcomment",
|
||||
"-predboolothers",
|
||||
"-ifempty",
|
||||
"-unrecogcomments",
|
||||
|
||||
# we may want to remove these later
|
||||
"-type",
|
||||
"-fixedformalarray",
|
||||
"-fullinitblock",
|
||||
"-fcnuse",
|
||||
"-initallelements",
|
||||
"-castfcnptr",
|
||||
# -forcehints,
|
||||
"-bufferoverflowhigh", # warns a lot about sprintf()
|
||||
|
||||
# re-definitions, rna causes most of these
|
||||
"-redef",
|
||||
"-syntax",
|
||||
|
||||
# dummy, witjout this splint complains with:
|
||||
# /usr/include/bits/confname.h:31:27: *** Internal Bug at cscannerHelp.c:2428: Unexpanded macro not function or constant: int _PC_MAX_CANON
|
||||
"-D_PC_MAX_CANON=0",
|
||||
]
|
||||
|
||||
|
||||
import project_source_info
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
USE_QUIET = (os.environ.get("QUIET", None) is not None)
|
||||
|
||||
|
||||
def main():
|
||||
source_info = project_source_info.build_info(use_cxx=False, ignore_prefix_list=CHECKER_IGNORE_PREFIX)
|
||||
|
||||
check_commands = []
|
||||
for c, inc_dirs, defs in source_info:
|
||||
cmd = ([CHECKER_BIN] +
|
||||
CHECKER_ARGS +
|
||||
[c] +
|
||||
[("-I%s" % i) for i in inc_dirs] +
|
||||
[("-D%s" % d) for d in defs]
|
||||
)
|
||||
|
||||
check_commands.append((c, cmd))
|
||||
|
||||
def my_process(i, c, cmd):
|
||||
if not USE_QUIET:
|
||||
percent = 100.0 * (i / len(check_commands))
|
||||
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
|
||||
|
||||
sys.stdout.write("%s %s\n" % (percent_str, c))
|
||||
sys.stdout.flush()
|
||||
|
||||
return subprocess.Popen(cmd)
|
||||
|
||||
process_functions = []
|
||||
for i, (c, cmd) in enumerate(check_commands):
|
||||
process_functions.append((my_process, (i, c, cmd)))
|
||||
|
||||
project_source_info.queue_processes(process_functions)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -865,29 +865,40 @@ Unfortunate Corner Cases
|
|||
Besides all expected cases listed above, there are a few others that should not be
|
||||
an issue but, due to internal implementation details, currently are:
|
||||
|
||||
- ``Object.hide_viewport``, ``Object.hide_select`` and ``Object.hide_render``:
|
||||
Setting any of those Booleans will trigger a rebuild of Collection caches,
|
||||
thus breaking any current iteration over ``Collection.all_objects``.
|
||||
|
||||
Collection Objects
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Changing: ``Object.hide_viewport``, ``Object.hide_select`` or ``Object.hide_render``
|
||||
will trigger a rebuild of Collection caches, thus breaking any current iteration over ``Collection.all_objects``.
|
||||
|
||||
.. rubric:: Do not:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# `all_objects` is an iterator. Using it directly while performing operations on its members that will update
|
||||
# the memory accessed by the `all_objects` iterator will lead to invalid memory accesses and crashes.
|
||||
for object in bpy.data.collections["Collection"].all_objects:
|
||||
object.hide_viewport = True
|
||||
|
||||
|
||||
.. rubric:: Do not:
|
||||
.. rubric:: Do:
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: python
|
||||
|
||||
# `all_objects` is an iterator. Using it directly while performing operations on its members that will update
|
||||
# the memory accessed by the `all_objects` iterator will lead to invalid memory accesses and crashes.
|
||||
for object in bpy.data.collections["Collection"].all_objects:
|
||||
object.hide_viewport = True
|
||||
# `all_objects[:]` is an independent list generated from the iterator. As long as no objects are deleted,
|
||||
# its content will remain valid even if the data accessed by the `all_objects` iterator is modified.
|
||||
for object in bpy.data.collections["Collection"].all_objects[:]:
|
||||
object.hide_viewport = True
|
||||
|
||||
|
||||
.. rubric:: Do:
|
||||
Data-Blocks Renaming During Iteration
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# `all_objects[:]` is an independent list generated from the iterator. As long as no objects are deleted,
|
||||
# its content will remain valid even if the data accessed by the `all_objects` iterator is modified.
|
||||
for object in bpy.data.collections["Collection"].all_objects[:]:
|
||||
object.hide_viewport = True
|
||||
Data-blocks accessed from ``bpy.data`` are sorted when their name is set.
|
||||
Any loop that iterates of a data such as ``bpy.data.objects`` for example,
|
||||
and sets the objects ``name`` must get all items from the iterator first (typically by converting to a list or tuple)
|
||||
to avoid missing some objects and iterating over others multiple times.
|
||||
|
||||
|
||||
sys.exit
|
||||
|
|
|
@ -113,9 +113,13 @@ BVHEmbree::~BVHEmbree()
|
|||
}
|
||||
}
|
||||
|
||||
void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_)
|
||||
void BVHEmbree::build(Progress &progress,
|
||||
Stats *stats,
|
||||
RTCDevice rtc_device_,
|
||||
const bool rtc_device_is_sycl_)
|
||||
{
|
||||
rtc_device = rtc_device_;
|
||||
rtc_device_is_sycl = rtc_device_is_sycl_;
|
||||
assert(rtc_device);
|
||||
|
||||
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
|
||||
|
@ -268,15 +272,29 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
|
|||
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
|
||||
|
||||
const int *triangles = mesh->get_triangles().data();
|
||||
rtcSetSharedGeometryBuffer(geom_id,
|
||||
RTC_BUFFER_TYPE_INDEX,
|
||||
0,
|
||||
RTC_FORMAT_UINT3,
|
||||
triangles,
|
||||
0,
|
||||
sizeof(int) * 3,
|
||||
num_triangles);
|
||||
|
||||
if (!rtc_device_is_sycl) {
|
||||
rtcSetSharedGeometryBuffer(geom_id,
|
||||
RTC_BUFFER_TYPE_INDEX,
|
||||
0,
|
||||
RTC_FORMAT_UINT3,
|
||||
triangles,
|
||||
0,
|
||||
sizeof(int) * 3,
|
||||
num_triangles);
|
||||
}
|
||||
else {
|
||||
/* NOTE(sirgienko): If the Embree device is a SYCL device, then Embree execution will
|
||||
* happen on GPU, and we cannot use standard host pointers at this point. So instead
|
||||
* of making a shared geometry buffer - a new Embree buffer will be created and data
|
||||
* will be copied. */
|
||||
int *triangles_buffer = (int *)rtcSetNewGeometryBuffer(
|
||||
geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(int) * 3, num_triangles);
|
||||
assert(triangles_buffer);
|
||||
if (triangles_buffer) {
|
||||
static_assert(sizeof(int) == sizeof(uint));
|
||||
std::memcpy(triangles_buffer, triangles, sizeof(int) * 3 * (num_triangles));
|
||||
}
|
||||
}
|
||||
set_tri_vertex_buffer(geom_id, mesh, false);
|
||||
|
||||
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
|
||||
|
@ -325,14 +343,38 @@ void BVHEmbree::set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, con
|
|||
rtcUpdateGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
|
||||
}
|
||||
else {
|
||||
rtcSetSharedGeometryBuffer(geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT3,
|
||||
verts,
|
||||
0,
|
||||
sizeof(float3),
|
||||
num_verts + 1);
|
||||
if (!rtc_device_is_sycl) {
|
||||
rtcSetSharedGeometryBuffer(geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT3,
|
||||
verts,
|
||||
0,
|
||||
sizeof(float3),
|
||||
num_verts + 1);
|
||||
}
|
||||
else {
|
||||
/* NOTE(sirgienko): If the Embree device is a SYCL device, then Embree execution will
|
||||
* happen on GPU, and we cannot use standard host pointers at this point. So instead
|
||||
* of making a shared geometry buffer - a new Embree buffer will be created and data
|
||||
* will be copied. */
|
||||
/* As float3 is packed on GPU side, we map it to packed_float3. */
|
||||
packed_float3 *verts_buffer = (packed_float3 *)rtcSetNewGeometryBuffer(
|
||||
geom_id,
|
||||
RTC_BUFFER_TYPE_VERTEX,
|
||||
t,
|
||||
RTC_FORMAT_FLOAT3,
|
||||
sizeof(packed_float3),
|
||||
num_verts + 1);
|
||||
assert(verts_buffer);
|
||||
if (verts_buffer) {
|
||||
for (size_t i = (size_t)0; i < num_verts + 1; ++i) {
|
||||
verts_buffer[i].x = verts[i].x;
|
||||
verts_buffer[i].y = verts[i].y;
|
||||
verts_buffer[i].z = verts[i].z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,10 @@ class Device;
|
|||
|
||||
class BVHEmbree : public BVH {
|
||||
public:
|
||||
void build(Progress &progress, Stats *stats, RTCDevice rtc_device);
|
||||
void build(Progress &progress,
|
||||
Stats *stats,
|
||||
RTCDevice rtc_device,
|
||||
const bool isSyclEmbreeDevice = false);
|
||||
void refit(Progress &progress);
|
||||
|
||||
RTCScene scene;
|
||||
|
@ -57,6 +60,7 @@ class BVHEmbree : public BVH {
|
|||
const bool update);
|
||||
|
||||
RTCDevice rtc_device;
|
||||
bool rtc_device_is_sycl;
|
||||
enum RTCBuildQuality build_quality;
|
||||
const Device *device;
|
||||
};
|
||||
|
|
|
@ -143,7 +143,7 @@ void OneapiDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
|
|||
bvh_embree->refit(progress);
|
||||
}
|
||||
else {
|
||||
bvh_embree->build(progress, &stats, embree_device);
|
||||
bvh_embree->build(progress, &stats, embree_device, true);
|
||||
}
|
||||
if (bvh->params.top_level) {
|
||||
embree_scene = bvh_embree->scene;
|
||||
|
|
|
@ -778,8 +778,6 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
|
|||
|
||||
# Host execution won't use GPU binaries, no need to compile them.
|
||||
if(WITH_CYCLES_ONEAPI_BINARIES AND NOT WITH_CYCLES_ONEAPI_HOST_TASK_EXECUTION)
|
||||
# AoT binaries aren't currently reused when calling sycl::build.
|
||||
list(APPEND sycl_compiler_flags -DWITH_CYCLES_ONEAPI_BINARIES)
|
||||
# Iterate over all targest and their options
|
||||
list(JOIN CYCLES_ONEAPI_SYCL_TARGETS "," targets_string)
|
||||
list(APPEND sycl_compiler_flags -fsycl-targets=${targets_string})
|
||||
|
|
|
@ -289,6 +289,15 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
|
|||
return false;
|
||||
}
|
||||
|
||||
# ifdef __EMBREE__
|
||||
IF_USING_EMBREE
|
||||
{
|
||||
if (kernel_data.device_bvh) {
|
||||
return kernel_embree_intersect_volume(kg, ray, isect, visibility);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
IF_NOT_USING_EMBREE
|
||||
{
|
||||
# ifdef __OBJECT_MOTION__
|
||||
|
|
|
@ -109,7 +109,9 @@ struct CCLVolumeContext
|
|||
#if EMBREE_MAJOR_VERSION >= 4
|
||||
KernelGlobals kg;
|
||||
const Ray *ray;
|
||||
# ifdef __VOLUME_RECORD_ALL__
|
||||
numhit_t max_hits;
|
||||
# endif
|
||||
numhit_t num_hits;
|
||||
#endif
|
||||
Intersection *vol_isect;
|
||||
|
@ -505,8 +507,10 @@ ccl_device_forceinline void kernel_embree_filter_occluded_volume_all_func_impl(
|
|||
#endif
|
||||
const Ray *cray = ctx->ray;
|
||||
|
||||
#ifdef __VOLUME_RECORD_ALL__
|
||||
/* Append the intersection to the end of the array. */
|
||||
if (ctx->num_hits < ctx->max_hits) {
|
||||
#endif
|
||||
Intersection current_isect;
|
||||
kernel_embree_convert_hit(
|
||||
kg, ray, hit, ¤t_isect, reinterpret_cast<intptr_t>(args->geometryUserPtr));
|
||||
|
@ -523,10 +527,17 @@ ccl_device_forceinline void kernel_embree_filter_occluded_volume_all_func_impl(
|
|||
int object_flag = kernel_data_fetch(object_flag, tri_object);
|
||||
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
|
||||
--ctx->num_hits;
|
||||
#ifndef __VOLUME_RECORD_ALL__
|
||||
/* Without __VOLUME_RECORD_ALL__ we need only a first counted hit, so we will
|
||||
* continue tracing only if a current hit is not counted. */
|
||||
*args->valid = 0;
|
||||
#endif
|
||||
}
|
||||
#ifdef __VOLUME_RECORD_ALL__
|
||||
/* This tells Embree to continue tracing. */
|
||||
*args->valid = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if EMBREE_MAJOR_VERSION < 4
|
||||
|
@ -844,7 +855,9 @@ ccl_device_intersect bool kernel_embree_intersect_shadow_all(KernelGlobals kg,
|
|||
ccl_device_intersect uint kernel_embree_intersect_volume(KernelGlobals kg,
|
||||
ccl_private const Ray *ray,
|
||||
ccl_private Intersection *isect,
|
||||
# ifdef __VOLUME_RECORD_ALL__
|
||||
const uint max_hits,
|
||||
# endif
|
||||
const uint visibility)
|
||||
{
|
||||
# if EMBREE_MAJOR_VERSION >= 4
|
||||
|
@ -864,7 +877,9 @@ ccl_device_intersect uint kernel_embree_intersect_volume(KernelGlobals kg,
|
|||
rtcInitIntersectContext(&ctx);
|
||||
# endif
|
||||
ctx.vol_isect = isect;
|
||||
# ifdef __VOLUME_RECORD_ALL__
|
||||
ctx.max_hits = numhit_t(max_hits);
|
||||
# endif
|
||||
ctx.num_hits = numhit_t(0);
|
||||
ctx.ray = ray;
|
||||
RTCRay rtc_ray;
|
||||
|
|
|
@ -174,6 +174,14 @@ 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
bool oneapi_kernel_is_using_embree(const std::string &kernel_name)
|
||||
{
|
||||
# ifdef WITH_EMBREE_GPU
|
||||
|
@ -182,8 +190,7 @@ bool oneapi_kernel_is_using_embree(const std::string &kernel_name)
|
|||
DeviceKernel kernel = (DeviceKernel)i;
|
||||
if (device_kernel_has_intersection(kernel)) {
|
||||
if (kernel_name.find(device_kernel_as_string(kernel)) != std::string::npos) {
|
||||
return !(kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE ||
|
||||
kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE);
|
||||
return !oneapi_kernel_is_raytrace_or_mnee(kernel_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -241,10 +248,6 @@ bool oneapi_load_kernels(SyclQueue *queue_,
|
|||
}
|
||||
# endif
|
||||
|
||||
# ifdef WITH_CYCLES_ONEAPI_BINARIES
|
||||
(void)queue_;
|
||||
(void)kernel_features;
|
||||
# else
|
||||
try {
|
||||
sycl::kernel_bundle<sycl::bundle_state::input> all_kernels_bundle =
|
||||
sycl::get_kernel_bundle<sycl::bundle_state::input>(queue->get_context(),
|
||||
|
@ -260,18 +263,22 @@ bool oneapi_load_kernels(SyclQueue *queue_,
|
|||
continue;
|
||||
}
|
||||
|
||||
sycl::kernel_bundle<sycl::bundle_state::input> one_kernel_bundle_input =
|
||||
sycl::get_kernel_bundle<sycl::bundle_state::input>(queue->get_context(), {kernel_id});
|
||||
# ifdef WITH_EMBREE_GPU
|
||||
/* This is expected to be the default, we set it again to be sure. */
|
||||
if (one_kernel_bundle_input
|
||||
.has_specialization_constant<ONEAPIKernelContext::oneapi_embree_features>()) {
|
||||
# ifdef WITH_EMBREE_GPU
|
||||
if (oneapi_kernel_is_using_embree(kernel_name) ||
|
||||
oneapi_kernel_is_raytrace_or_mnee(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
|
||||
.set_specialization_constant<ONEAPIKernelContext::oneapi_embree_features>(
|
||||
RTC_FEATURE_FLAG_NONE);
|
||||
sycl::build(one_kernel_bundle_input);
|
||||
continue;
|
||||
}
|
||||
# endif
|
||||
sycl::build(one_kernel_bundle_input);
|
||||
# endif
|
||||
/* This call will ensure that AoT or cached JIT binaries are available
|
||||
* for execution. It will trigger compilation if it is not already the case. */
|
||||
(void)sycl::get_kernel_bundle<sycl::bundle_state::executable>(queue->get_context(),
|
||||
{kernel_id});
|
||||
}
|
||||
}
|
||||
catch (sycl::exception const &e) {
|
||||
|
@ -280,7 +287,6 @@ bool oneapi_load_kernels(SyclQueue *queue_,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
# endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -557,7 +557,7 @@ static void log_kernel_features(const uint features)
|
|||
<< "\n";
|
||||
VLOG_INFO << "Use Shader Raytrace " << string_from_bool(features & KERNEL_FEATURE_NODE_RAYTRACE)
|
||||
<< "\n";
|
||||
VLOG_INFO << "Use MNEE" << string_from_bool(features & KERNEL_FEATURE_MNEE) << "\n";
|
||||
VLOG_INFO << "Use MNEE " << string_from_bool(features & KERNEL_FEATURE_MNEE) << "\n";
|
||||
VLOG_INFO << "Use Transparent " << string_from_bool(features & KERNEL_FEATURE_TRANSPARENT)
|
||||
<< "\n";
|
||||
VLOG_INFO << "Use Denoising " << string_from_bool(features & KERNEL_FEATURE_DENOISING) << "\n";
|
||||
|
|
|
@ -72,7 +72,7 @@ class GHOST_ISystemPaths {
|
|||
/**
|
||||
* Add the file to the operating system most recently used files
|
||||
*/
|
||||
virtual void addToSystemRecentFiles(const char *filename) const = 0;
|
||||
virtual void addToSystemRecentFiles(const char *filepath) const = 0;
|
||||
|
||||
private:
|
||||
/** The one and only system paths. */
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#include "GHOST_Context.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
|
||||
# include <epoxy/wgl.h>
|
||||
# include <tchar.h>
|
||||
#
|
||||
|
@ -18,6 +21,8 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
#include "GHOST_IContext.hh"
|
||||
#include "GHOST_Types.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#include <cstdlib> // for NULL
|
||||
|
||||
class GHOST_Context : public GHOST_IContext {
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <Metal/Metal.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
|
|
|
@ -903,13 +903,14 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext()
|
|||
auto extensions_available = getExtensionsAvailable();
|
||||
|
||||
vector<const char *> layers_enabled;
|
||||
if (m_debug) {
|
||||
enableLayer(layers_available, layers_enabled, VkLayer::KHRONOS_validation, m_debug);
|
||||
}
|
||||
|
||||
vector<const char *> extensions_device;
|
||||
vector<const char *> extensions_enabled;
|
||||
|
||||
if (m_debug) {
|
||||
enableLayer(layers_available, layers_enabled, VkLayer::KHRONOS_validation, m_debug);
|
||||
requireExtension(extensions_available, extensions_enabled, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
if (use_window_surface) {
|
||||
const char *native_surface_extension_name = getPlatformSpecificSurfaceExtension();
|
||||
|
||||
|
|
|
@ -47,5 +47,5 @@ class GHOST_SystemPaths : public GHOST_ISystemPaths {
|
|||
/**
|
||||
* Add the file to the operating system most recently used files
|
||||
*/
|
||||
virtual void addToSystemRecentFiles(const char *filename) const = 0;
|
||||
virtual void addToSystemRecentFiles(const char *filepath) const = 0;
|
||||
};
|
||||
|
|
|
@ -54,5 +54,5 @@ class GHOST_SystemPathsCocoa : public GHOST_SystemPaths {
|
|||
/**
|
||||
* Add the file to the operating system most recently used files
|
||||
*/
|
||||
void addToSystemRecentFiles(const char *filename) const;
|
||||
void addToSystemRecentFiles(const char *filepath) const;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2010 Blender Foundation */
|
||||
|
||||
#import <AppKit/NSDocumentController.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "GHOST_Debug.hh"
|
||||
|
@ -112,7 +113,10 @@ const char *GHOST_SystemPathsCocoa::getBinaryDir() const
|
|||
return tempPath;
|
||||
}
|
||||
|
||||
void GHOST_SystemPathsCocoa::addToSystemRecentFiles(const char *filename) const
|
||||
void GHOST_SystemPathsCocoa::addToSystemRecentFiles(const char *filepath) const
|
||||
{
|
||||
/* TODO: implement for macOS */
|
||||
@autoreleasepool {
|
||||
NSURL *const file_url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:filepath]];
|
||||
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:file_url];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,5 +52,5 @@ class GHOST_SystemPathsUnix : public GHOST_SystemPaths {
|
|||
/**
|
||||
* Add the file to the operating system most recently used files
|
||||
*/
|
||||
void addToSystemRecentFiles(const char *filename) const;
|
||||
void addToSystemRecentFiles(const char *filepath) const;
|
||||
};
|
||||
|
|
|
@ -117,10 +117,10 @@ const char *GHOST_SystemPathsWin32::getBinaryDir() const
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void GHOST_SystemPathsWin32::addToSystemRecentFiles(const char *filename) const
|
||||
void GHOST_SystemPathsWin32::addToSystemRecentFiles(const char *filepath) const
|
||||
{
|
||||
/* SHARD_PATH resolves to SHARD_PATHA for non-UNICODE build */
|
||||
UTF16_ENCODE(filename);
|
||||
SHAddToRecentDocs(SHARD_PATHW, filename_16);
|
||||
UTF16_UN_ENCODE(filename);
|
||||
UTF16_ENCODE(filepath);
|
||||
SHAddToRecentDocs(SHARD_PATHW, filepath_16);
|
||||
UTF16_UN_ENCODE(filepath);
|
||||
}
|
||||
|
|
|
@ -61,5 +61,5 @@ class GHOST_SystemPathsWin32 : public GHOST_SystemPaths {
|
|||
/**
|
||||
* Add the file to the operating system most recently used files
|
||||
*/
|
||||
void addToSystemRecentFiles(const char *filename) const;
|
||||
void addToSystemRecentFiles(const char *filepath) const;
|
||||
};
|
||||
|
|
|
@ -99,7 +99,12 @@ static bool use_xwayland_hack = false;
|
|||
|
||||
using namespace std;
|
||||
|
||||
GHOST_SystemX11::GHOST_SystemX11() : GHOST_System(), m_xkb_descr(nullptr), m_start_time(0)
|
||||
GHOST_SystemX11::GHOST_SystemX11()
|
||||
: GHOST_System(),
|
||||
m_xkb_descr(nullptr),
|
||||
m_start_time(0),
|
||||
m_keyboard_vector{0},
|
||||
m_keycode_last_repeat_key(uint(-1))
|
||||
{
|
||||
XInitThreads();
|
||||
m_display = XOpenDisplay(nullptr);
|
||||
|
@ -897,7 +902,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
|
|||
#endif /* WITH_X11_XINPUT */
|
||||
switch (xe->type) {
|
||||
case Expose: {
|
||||
XExposeEvent &xee = xe->xexpose;
|
||||
const XExposeEvent &xee = xe->xexpose;
|
||||
|
||||
if (xee.count == 0) {
|
||||
/* Only generate a single expose event
|
||||
|
@ -909,7 +914,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
|
|||
}
|
||||
|
||||
case MotionNotify: {
|
||||
XMotionEvent &xme = xe->xmotion;
|
||||
const XMotionEvent &xme = xe->xmotion;
|
||||
|
||||
bool is_tablet = window->GetTabletData().Active != GHOST_kTabletModeNone;
|
||||
|
||||
|
@ -1235,7 +1240,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
|
|||
|
||||
case ButtonPress:
|
||||
case ButtonRelease: {
|
||||
XButtonEvent &xbe = xe->xbutton;
|
||||
const XButtonEvent &xbe = xe->xbutton;
|
||||
GHOST_TButton gbmask = GHOST_kButtonMaskLeft;
|
||||
GHOST_TEventType type = (xbe.type == ButtonPress) ? GHOST_kEventButtonDown :
|
||||
GHOST_kEventButtonUp;
|
||||
|
@ -1290,14 +1295,14 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
|
|||
|
||||
/* change of size, border, layer etc. */
|
||||
case ConfigureNotify: {
|
||||
// XConfigureEvent & xce = xe->xconfigure;
|
||||
// const XConfigureEvent & xce = xe->xconfigure;
|
||||
g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window);
|
||||
break;
|
||||
}
|
||||
|
||||
case FocusIn:
|
||||
case FocusOut: {
|
||||
XFocusChangeEvent &xfe = xe->xfocus;
|
||||
const XFocusChangeEvent &xfe = xe->xfocus;
|
||||
|
||||
/* TODO: make sure this is the correct place for activate/deactivate */
|
||||
// printf("X: focus %s for window %d\n",
|
||||
|
@ -1385,7 +1390,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
|
|||
* (really crossing between windows) since some window-managers
|
||||
* also send grab/un-grab crossings for mouse-wheel events.
|
||||
*/
|
||||
XCrossingEvent &xce = xe->xcrossing;
|
||||
const XCrossingEvent &xce = xe->xcrossing;
|
||||
if (xce.mode == NotifyNormal) {
|
||||
g_event = new GHOST_EventCursor(getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
|
@ -2376,7 +2381,7 @@ class DialogData {
|
|||
}
|
||||
|
||||
/* Is the mouse inside the given button */
|
||||
bool isInsideButton(XEvent &e, uint button_num)
|
||||
bool isInsideButton(const XEvent &e, uint button_num)
|
||||
{
|
||||
return (
|
||||
(e.xmotion.y > int(height - padding_y - button_height)) &&
|
||||
|
|
|
@ -21,22 +21,21 @@ GHOST_Window::GHOST_Window(uint32_t width,
|
|||
const bool wantStereoVisual,
|
||||
const bool /*exclusive*/)
|
||||
: m_drawingContextType(GHOST_kDrawingContextTypeNone),
|
||||
m_userData(nullptr),
|
||||
m_cursorVisible(true),
|
||||
m_cursorGrab(GHOST_kGrabDisable),
|
||||
m_cursorGrabAxis(GHOST_kAxisNone),
|
||||
m_cursorGrabInitPos{0, 0},
|
||||
m_cursorGrabAccumPos{0, 0},
|
||||
m_cursorShape(GHOST_kStandardCursorDefault),
|
||||
m_progressBarVisible(false),
|
||||
m_canAcceptDragOperation(false),
|
||||
m_isUnsavedChanges(false),
|
||||
m_wantStereoVisual(wantStereoVisual),
|
||||
m_nativePixelSize(1.0f),
|
||||
m_context(new GHOST_ContextNone(false))
|
||||
|
||||
{
|
||||
m_isUnsavedChanges = false;
|
||||
m_canAcceptDragOperation = false;
|
||||
|
||||
m_progressBarVisible = false;
|
||||
|
||||
m_cursorGrabAccumPos[0] = 0;
|
||||
m_cursorGrabAccumPos[1] = 0;
|
||||
|
||||
m_nativePixelSize = 1.0f;
|
||||
|
||||
m_fullScreen = state == GHOST_kWindowStateFullScreen;
|
||||
if (m_fullScreen) {
|
||||
|
|
|
@ -6848,6 +6848,17 @@ def km_3d_view_tool_cursor(params):
|
|||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_text_select(params):
|
||||
return (
|
||||
"3D View Tool: Edit Text, Select Text",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("font.selection_set", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
|
||||
("font.select_word", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_select(params, *, fallback):
|
||||
if params.use_tweak_select_passthrough:
|
||||
operator_props = (("vert_without_handles", True),)
|
||||
|
@ -8264,6 +8275,7 @@ def generate_keymaps(params=None):
|
|||
*(km_node_editor_tool_select_circle(params, fallback=fallback) for fallback in (False, True)),
|
||||
km_node_editor_tool_links_cut(params),
|
||||
km_3d_view_tool_cursor(params),
|
||||
km_3d_view_tool_text_select(params),
|
||||
*(km_3d_view_tool_select(params, fallback=fallback) for fallback in (False, True)),
|
||||
*(km_3d_view_tool_select_box(params, fallback=fallback) for fallback in (False, True)),
|
||||
*(km_3d_view_tool_select_circle(params, fallback=fallback) for fallback in (False, True)),
|
||||
|
|
|
@ -220,6 +220,7 @@ class NODE_MT_geometry_node_GEO_GEOMETRY_SAMPLE(Menu):
|
|||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeProximity")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeIndexOfNearest")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeRaycast")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSampleIndex")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSampleNearest")
|
||||
|
|
|
@ -1284,6 +1284,20 @@ class _defs_edit_curve:
|
|||
)
|
||||
|
||||
|
||||
class _defs_edit_text:
|
||||
|
||||
@ToolDef.from_fn
|
||||
def select_text():
|
||||
return dict(
|
||||
idname="builtin.select_text",
|
||||
label="Select Text",
|
||||
cursor='TEXT',
|
||||
icon="ops.generic.select_box",
|
||||
widget=None,
|
||||
keymap=(),
|
||||
)
|
||||
|
||||
|
||||
class _defs_pose:
|
||||
|
||||
@ToolDef.from_fn
|
||||
|
@ -2980,6 +2994,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
_defs_transform.shear,
|
||||
],
|
||||
'EDIT_TEXT': [
|
||||
_defs_edit_text.select_text,
|
||||
_defs_view3d_generic.cursor,
|
||||
None,
|
||||
*_tools_annotate,
|
||||
|
|
|
@ -23,7 +23,7 @@ std::string normalize_directory_path(StringRef directory)
|
|||
directory.data(),
|
||||
/* + 1 for null terminator. */
|
||||
std::min(directory.size() + 1, int64_t(sizeof(dir_normalized))));
|
||||
BLI_path_normalize_dir(nullptr, dir_normalized, sizeof(dir_normalized));
|
||||
BLI_path_normalize_dir(dir_normalized, sizeof(dir_normalized));
|
||||
return std::string(dir_normalized);
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ std::string normalize_path(StringRefNull path, int64_t max_len)
|
|||
|
||||
char *buf = BLI_strdupn(path.c_str(), len);
|
||||
BLI_path_slash_native(buf);
|
||||
BLI_path_normalize(nullptr, buf);
|
||||
BLI_path_normalize(buf);
|
||||
|
||||
std::string normalized_path = buf;
|
||||
MEM_freeN(buf);
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace blender::bke {
|
|||
*
|
||||
* Once created, #AnonymousAttributeID is immutable. Also it is intrinsically reference counted so
|
||||
* that it can have shared ownership. `std::shared_ptr` can't be used for that purpose here,
|
||||
* because that is not available in C code. If possible, the #AutoAnonymousAttributeID wrapper
|
||||
* because that is not available in C code. If possible, the #AnonymousAttributeIDPtr wrapper
|
||||
* should be used to avoid manual reference counting in C++ code.
|
||||
*/
|
||||
class AnonymousAttributeID : public ImplicitSharingMixin {
|
||||
|
@ -54,7 +54,7 @@ class AnonymousAttributeID : public ImplicitSharingMixin {
|
|||
};
|
||||
|
||||
/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */
|
||||
using AutoAnonymousAttributeID = ImplicitSharingPtr<const AnonymousAttributeID>;
|
||||
using AnonymousAttributeIDPtr = ImplicitSharingPtr<const AnonymousAttributeID>;
|
||||
|
||||
/**
|
||||
* A set of anonymous attribute names that is passed around in geometry nodes.
|
||||
|
|
|
@ -55,7 +55,7 @@ struct BasisCache {
|
|||
class CurvesGeometryRuntime {
|
||||
public:
|
||||
/** Implicit sharing user count for #CurvesGeometry::curve_offsets. */
|
||||
ImplicitSharingInfo *curve_offsets_sharing_info = nullptr;
|
||||
const ImplicitSharingInfo *curve_offsets_sharing_info = nullptr;
|
||||
|
||||
/**
|
||||
* The cached number of curves with each type. Unlike other caches here, this is not computed
|
||||
|
|
|
@ -259,11 +259,11 @@ class NormalFieldInput : public GeometryFieldInput {
|
|||
|
||||
class AnonymousAttributeFieldInput : public GeometryFieldInput {
|
||||
private:
|
||||
AutoAnonymousAttributeID anonymous_id_;
|
||||
AnonymousAttributeIDPtr anonymous_id_;
|
||||
std::string producer_name_;
|
||||
|
||||
public:
|
||||
AnonymousAttributeFieldInput(AutoAnonymousAttributeID anonymous_id,
|
||||
AnonymousAttributeFieldInput(AnonymousAttributeIDPtr anonymous_id,
|
||||
const CPPType &type,
|
||||
std::string producer_name)
|
||||
: GeometryFieldInput(type, anonymous_id->user_name()),
|
||||
|
@ -274,7 +274,7 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
static fn::Field<T> Create(AutoAnonymousAttributeID anonymous_id, std::string producer_name)
|
||||
static fn::Field<T> Create(AnonymousAttributeIDPtr anonymous_id, std::string producer_name)
|
||||
{
|
||||
const CPPType &type = CPPType::get<T>();
|
||||
auto field_input = std::make_shared<AnonymousAttributeFieldInput>(
|
||||
|
@ -282,7 +282,7 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
|
|||
return fn::Field<T>{field_input};
|
||||
}
|
||||
|
||||
const AutoAnonymousAttributeID &anonymous_id() const
|
||||
const AnonymousAttributeIDPtr &anonymous_id() const
|
||||
{
|
||||
return anonymous_id_;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
# include <mutex>
|
||||
|
||||
# include "MEM_guardedalloc.h"
|
||||
|
||||
# include "BLI_array.hh"
|
||||
# include "BLI_bit_vector.hh"
|
||||
# include "BLI_bounds_types.hh"
|
||||
|
@ -22,7 +20,6 @@
|
|||
# include "BLI_span.hh"
|
||||
# include "BLI_vector.hh"
|
||||
|
||||
# include "DNA_customdata_types.h"
|
||||
# include "DNA_meshdata_types.h"
|
||||
|
||||
struct BVHCache;
|
||||
|
@ -69,20 +66,25 @@ namespace blender::bke {
|
|||
/**
|
||||
* Cache of a mesh's loose edges, accessed with #Mesh::loose_edges(). *
|
||||
*/
|
||||
struct LooseEdgeCache {
|
||||
struct LooseGeomCache {
|
||||
/**
|
||||
* A bitmap set to true for each loose edge, false if the edge is used by any face.
|
||||
* Allocated only if there is at least one loose edge.
|
||||
* A bitmap set to true for each loose element, false if the element is used by any face.
|
||||
* Allocated only if there is at least one loose element.
|
||||
*/
|
||||
blender::BitVector<> is_loose_bits;
|
||||
/**
|
||||
* The number of loose edges. If zero, the #is_loose_bits shouldn't be accessed.
|
||||
* The number of loose elements. If zero, the #is_loose_bits shouldn't be accessed.
|
||||
* If less than zero, the cache has been accessed in an invalid way
|
||||
* (i.e.directly instead of through #Mesh::loose_edges()).
|
||||
*/
|
||||
int count = -1;
|
||||
};
|
||||
|
||||
struct LooseEdgeCache : public LooseGeomCache {
|
||||
};
|
||||
struct LooseVertCache : public LooseGeomCache {
|
||||
};
|
||||
|
||||
struct MeshRuntime {
|
||||
/* Evaluated mesh for objects which do not have effective modifiers.
|
||||
* This mesh is used as a result of modifier stack evaluation.
|
||||
|
@ -98,7 +100,7 @@ struct MeshRuntime {
|
|||
std::mutex render_mutex;
|
||||
|
||||
/** Implicit sharing user count for #Mesh::poly_offset_indices. */
|
||||
ImplicitSharingInfoHandle *poly_offsets_sharing_info;
|
||||
const ImplicitSharingInfoHandle *poly_offsets_sharing_info;
|
||||
|
||||
/**
|
||||
* A cache of bounds shared between data-blocks with unchanged positions. When changing positions
|
||||
|
@ -166,11 +168,12 @@ struct MeshRuntime {
|
|||
mutable Vector<float3> vert_normals;
|
||||
mutable Vector<float3> poly_normals;
|
||||
|
||||
/**
|
||||
* A cache of data about the loose edges. Can be shared with other data-blocks with unchanged
|
||||
* topology. Accessed with #Mesh::loose_edges().
|
||||
*/
|
||||
/** Cache of data about edges not used by faces. See #Mesh::loose_edges(). */
|
||||
SharedCache<LooseEdgeCache> loose_edges_cache;
|
||||
/** Cache of data about vertices not used by edges. See #Mesh::loose_verts(). */
|
||||
SharedCache<LooseVertCache> loose_verts_cache;
|
||||
/** Cache of data about vertices not used by faces. See #Mesh::verts_no_face(). */
|
||||
SharedCache<LooseVertCache> verts_no_face_cache;
|
||||
|
||||
/**
|
||||
* A bit vector the size of the number of vertices, set to true for the center vertices of
|
||||
|
|
|
@ -1581,6 +1581,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
|
|||
#define GEO_NODE_SDF_VOLUME_SPHERE 1196
|
||||
#define GEO_NODE_MEAN_FILTER_SDF_VOLUME 1197
|
||||
#define GEO_NODE_OFFSET_SDF_VOLUME 1198
|
||||
#define GEO_NODE_INDEX_OF_NEAREST 1199
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -154,6 +154,11 @@ void BKE_sound_set_scene_sound_volume(void *handle, float volume, char animated)
|
|||
|
||||
void BKE_sound_set_scene_sound_pitch(void *handle, float pitch, char animated);
|
||||
|
||||
void BKE_sound_set_scene_sound_pitch_constant_range(void *handle,
|
||||
int frame_start,
|
||||
int frame_end,
|
||||
float pitch);
|
||||
|
||||
void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated);
|
||||
|
||||
void BKE_sound_update_sequencer(struct Main *main, struct bSound *sound);
|
||||
|
|
|
@ -89,6 +89,9 @@ bool BKE_vfont_to_curve_ex(struct Object *ob,
|
|||
bool *r_text_free,
|
||||
struct CharTrans **r_chartransdata);
|
||||
bool BKE_vfont_to_curve_nubase(struct Object *ob, int mode, struct ListBase *r_nubase);
|
||||
|
||||
int BKE_vfont_cursor_to_text_index(struct Object *ob, float cursor_location[2]);
|
||||
|
||||
/**
|
||||
* \warning Expects to have access to evaluated data (i.e. passed object should be evaluated one).
|
||||
*/
|
||||
|
|
|
@ -569,6 +569,27 @@ static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md,
|
|||
return mesh_output;
|
||||
}
|
||||
|
||||
static void set_rest_position(Mesh &mesh)
|
||||
{
|
||||
using namespace blender;
|
||||
using namespace blender::bke;
|
||||
MutableAttributeAccessor attributes = mesh.attributes_for_write();
|
||||
const AttributeReader positions = attributes.lookup<float3>("position");
|
||||
attributes.remove("rest_position");
|
||||
if (positions) {
|
||||
if (positions.sharing_info && positions.varray.is_span()) {
|
||||
attributes.add<float3>("rest_position",
|
||||
ATTR_DOMAIN_POINT,
|
||||
AttributeInitShared(positions.varray.get_internal_span().data(),
|
||||
*positions.sharing_info));
|
||||
}
|
||||
else {
|
||||
attributes.add<float3>(
|
||||
"rest_position", ATTR_DOMAIN_POINT, AttributeInitVArray(positions.varray));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
|
||||
const Scene *scene,
|
||||
Object *ob,
|
||||
|
@ -657,20 +678,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
|
|||
mesh_final = BKE_mesh_copy_for_eval(mesh_input);
|
||||
ASSERT_IS_VALID_MESH(mesh_final);
|
||||
}
|
||||
MutableAttributeAccessor attributes = mesh_final->attributes_for_write();
|
||||
const AttributeReader positions = attributes.lookup<float3>("position");
|
||||
if (positions) {
|
||||
if (positions.sharing_info && positions.varray.is_span()) {
|
||||
attributes.add<float3>("rest_position",
|
||||
ATTR_DOMAIN_POINT,
|
||||
AttributeInitShared(positions.varray.get_internal_span().data(),
|
||||
*positions.sharing_info));
|
||||
}
|
||||
else {
|
||||
attributes.add<float3>(
|
||||
"rest_position", ATTR_DOMAIN_POINT, AttributeInitVArray(positions.varray));
|
||||
}
|
||||
}
|
||||
set_rest_position(*mesh_final);
|
||||
}
|
||||
|
||||
/* Apply all leading deform modifiers. */
|
||||
|
@ -1194,6 +1202,14 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
|
|||
/* Clear errors before evaluation. */
|
||||
BKE_modifiers_clear_errors(ob);
|
||||
|
||||
if (ob->modifier_flag & OB_MODIFIER_FLAG_ADD_REST_POSITION) {
|
||||
if (mesh_final == nullptr) {
|
||||
mesh_final = BKE_mesh_from_bmesh_for_eval_nomain(em_input->bm, nullptr, mesh_input);
|
||||
ASSERT_IS_VALID_MESH(mesh_final);
|
||||
}
|
||||
set_rest_position(*mesh_final);
|
||||
}
|
||||
|
||||
for (int i = 0; md; i++, md = md->next, md_datamask = md_datamask->next) {
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
||||
|
||||
|
|
|
@ -386,7 +386,7 @@ static bool get_path_local_ex(char *targetpath,
|
|||
char osx_resourses[FILE_MAX + 4 + 9];
|
||||
BLI_path_join(osx_resourses, sizeof(osx_resourses), g_app.program_dirname, "..", "Resources");
|
||||
/* Remove the '/../' added above. */
|
||||
BLI_path_normalize(NULL, osx_resourses);
|
||||
BLI_path_normalize(osx_resourses);
|
||||
path_base = osx_resourses;
|
||||
#endif
|
||||
return test_path(targetpath,
|
||||
|
@ -871,7 +871,7 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
|
|||
BLI_path_program_search(fullname, maxlen, name);
|
||||
}
|
||||
/* Remove "/./" and "/../" so string comparisons can be used on the path. */
|
||||
BLI_path_normalize(NULL, fullname);
|
||||
BLI_path_normalize(fullname);
|
||||
|
||||
# if defined(DEBUG)
|
||||
if (!STREQ(name, fullname)) {
|
||||
|
@ -890,7 +890,7 @@ void BKE_appdir_program_path_init(const char *argv0)
|
|||
* which must point to the Python module for data-files to be detected. */
|
||||
STRNCPY(g_app.program_filepath, argv0);
|
||||
BLI_path_abs_from_cwd(g_app.program_filepath, sizeof(g_app.program_filepath));
|
||||
BLI_path_normalize(NULL, g_app.program_filepath);
|
||||
BLI_path_normalize(g_app.program_filepath);
|
||||
|
||||
if (g_app.program_dirname[0] == '\0') {
|
||||
/* First time initializing, the file binary path isn't valid from a Python module.
|
||||
|
|
|
@ -441,7 +441,7 @@ static bool relative_rebase_foreach_path_cb(BPathForeachPathData *bpath_data,
|
|||
return false;
|
||||
}
|
||||
|
||||
BLI_path_normalize(NULL, filepath);
|
||||
BLI_path_normalize(filepath);
|
||||
|
||||
/* This may fail, if so it's fine to leave absolute since the path is still valid. */
|
||||
BLI_path_rel(filepath, data->basedir_dst);
|
||||
|
|
|
@ -833,8 +833,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon,
|
|||
}
|
||||
|
||||
static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
|
||||
const blender::int2 *edge,
|
||||
const int edge_num,
|
||||
blender::Span<blender::int2> edges,
|
||||
const BitSpan edges_mask,
|
||||
int edges_num_active,
|
||||
float epsilon,
|
||||
|
@ -842,10 +841,10 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
|
|||
int axis)
|
||||
{
|
||||
if (!edges_mask.is_empty()) {
|
||||
BLI_assert(IN_RANGE_INCL(edges_num_active, 0, edge_num));
|
||||
BLI_assert(IN_RANGE_INCL(edges_num_active, 0, edges.size()));
|
||||
}
|
||||
else {
|
||||
edges_num_active = edge_num;
|
||||
edges_num_active = edges.size();
|
||||
}
|
||||
if (edges_num_active == 0) {
|
||||
return nullptr;
|
||||
|
@ -857,13 +856,13 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < edge_num; i++) {
|
||||
for (const int i : edges.index_range()) {
|
||||
if (!edges_mask.is_empty() && !edges_mask[i]) {
|
||||
continue;
|
||||
}
|
||||
float co[2][3];
|
||||
copy_v3_v3(co[0], positions[edge[i][0]]);
|
||||
copy_v3_v3(co[1], positions[edge[i][1]]);
|
||||
copy_v3_v3(co[0], positions[edges[i][0]]);
|
||||
copy_v3_v3(co[1], positions[edges[i][1]]);
|
||||
|
||||
BLI_bvhtree_insert(tree, i, co[0], 2);
|
||||
}
|
||||
|
@ -908,7 +907,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data,
|
|||
int axis)
|
||||
{
|
||||
BVHTree *tree = bvhtree_from_mesh_edges_create_tree(
|
||||
vert_positions, edge, edges_num, edges_mask, edges_num_active, epsilon, tree_type, axis);
|
||||
vert_positions, {edge, edges_num}, edges_mask, edges_num_active, epsilon, tree_type, axis);
|
||||
|
||||
bvhtree_balance(tree, false);
|
||||
|
||||
|
@ -1142,38 +1141,6 @@ BVHTree *bvhtree_from_mesh_looptri_ex(BVHTreeFromMesh *data,
|
|||
return tree;
|
||||
}
|
||||
|
||||
static BitVector<> loose_verts_map_get(const Span<blender::int2> edges,
|
||||
int verts_num,
|
||||
int *r_loose_vert_num)
|
||||
{
|
||||
BitVector<> loose_verts_mask(verts_num, true);
|
||||
|
||||
int num_linked_verts = 0;
|
||||
for (const int64_t i : edges.index_range()) {
|
||||
const blender::int2 &edge = edges[i];
|
||||
if (loose_verts_mask[edge[0]]) {
|
||||
loose_verts_mask[edge[0]].reset();
|
||||
num_linked_verts++;
|
||||
}
|
||||
if (loose_verts_mask[edge[1]]) {
|
||||
loose_verts_mask[edge[1]].reset();
|
||||
num_linked_verts++;
|
||||
}
|
||||
}
|
||||
|
||||
*r_loose_vert_num = verts_num - num_linked_verts;
|
||||
|
||||
return loose_verts_mask;
|
||||
}
|
||||
|
||||
static BitVector<> loose_edges_map_get(const Mesh &mesh, int *r_loose_edge_len)
|
||||
{
|
||||
using namespace blender::bke;
|
||||
const LooseEdgeCache &loose_edges = mesh.loose_edges();
|
||||
*r_loose_edge_len = loose_edges.count;
|
||||
return loose_edges.is_loose_bits;
|
||||
}
|
||||
|
||||
static BitVector<> looptri_no_hidden_map_get(const blender::OffsetIndices<int> polys,
|
||||
const VArray<bool> &hide_poly,
|
||||
const int looptri_len,
|
||||
|
@ -1243,27 +1210,36 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
|
|||
}
|
||||
|
||||
/* Create BVHTree. */
|
||||
BitVector<> mask;
|
||||
int mask_bits_act_len = -1;
|
||||
|
||||
switch (bvh_cache_type) {
|
||||
case BVHTREE_FROM_LOOSEVERTS:
|
||||
mask = loose_verts_map_get(edges, mesh->totvert, &mask_bits_act_len);
|
||||
ATTR_FALLTHROUGH;
|
||||
case BVHTREE_FROM_VERTS:
|
||||
case BVHTREE_FROM_LOOSEVERTS: {
|
||||
const blender::bke::LooseVertCache &loose_verts = mesh->loose_verts();
|
||||
data->tree = bvhtree_from_mesh_verts_create_tree(0.0f,
|
||||
tree_type,
|
||||
6,
|
||||
positions,
|
||||
mesh->totvert,
|
||||
loose_verts.is_loose_bits,
|
||||
loose_verts.count);
|
||||
break;
|
||||
}
|
||||
case BVHTREE_FROM_VERTS: {
|
||||
data->tree = bvhtree_from_mesh_verts_create_tree(
|
||||
0.0f, tree_type, 6, positions, mesh->totvert, mask, mask_bits_act_len);
|
||||
0.0f, tree_type, 6, positions, mesh->totvert, {}, -1);
|
||||
break;
|
||||
|
||||
case BVHTREE_FROM_LOOSEEDGES:
|
||||
mask = loose_edges_map_get(*mesh, &mask_bits_act_len);
|
||||
ATTR_FALLTHROUGH;
|
||||
case BVHTREE_FROM_EDGES:
|
||||
}
|
||||
case BVHTREE_FROM_LOOSEEDGES: {
|
||||
const blender::bke::LooseEdgeCache &loose_edges = mesh->loose_edges();
|
||||
data->tree = bvhtree_from_mesh_edges_create_tree(
|
||||
positions, edges.data(), mesh->totedge, mask, mask_bits_act_len, 0.0f, tree_type, 6);
|
||||
positions, edges, loose_edges.is_loose_bits, loose_edges.count, 0.0f, tree_type, 6);
|
||||
break;
|
||||
|
||||
case BVHTREE_FROM_FACES:
|
||||
}
|
||||
case BVHTREE_FROM_EDGES: {
|
||||
data->tree = bvhtree_from_mesh_edges_create_tree(
|
||||
positions, edges, {}, -1, 0.0f, tree_type, 6);
|
||||
break;
|
||||
}
|
||||
case BVHTREE_FROM_FACES: {
|
||||
BLI_assert(!(mesh->totface == 0 && mesh->totpoly != 0));
|
||||
data->tree = bvhtree_from_mesh_faces_create_tree(
|
||||
0.0f,
|
||||
|
@ -1275,25 +1251,29 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
|
|||
{},
|
||||
-1);
|
||||
break;
|
||||
|
||||
}
|
||||
case BVHTREE_FROM_LOOPTRI_NO_HIDDEN: {
|
||||
blender::bke::AttributeAccessor attributes = mesh->attributes();
|
||||
mask = looptri_no_hidden_map_get(
|
||||
int mask_bits_act_len = -1;
|
||||
const BitVector<> mask = looptri_no_hidden_map_get(
|
||||
mesh->polys(),
|
||||
*attributes.lookup_or_default(".hide_poly", ATTR_DOMAIN_FACE, false),
|
||||
looptris.size(),
|
||||
&mask_bits_act_len);
|
||||
ATTR_FALLTHROUGH;
|
||||
}
|
||||
case BVHTREE_FROM_LOOPTRI:
|
||||
data->tree = bvhtree_from_mesh_looptri_create_tree(
|
||||
0.0f, tree_type, 6, positions, corner_verts.data(), looptris, mask, mask_bits_act_len);
|
||||
break;
|
||||
}
|
||||
case BVHTREE_FROM_LOOPTRI: {
|
||||
data->tree = bvhtree_from_mesh_looptri_create_tree(
|
||||
0.0f, tree_type, 6, positions, corner_verts.data(), looptris, {}, -1);
|
||||
break;
|
||||
}
|
||||
case BVHTREE_FROM_EM_VERTS:
|
||||
case BVHTREE_FROM_EM_EDGES:
|
||||
case BVHTREE_FROM_EM_LOOPTRI:
|
||||
case BVHTREE_MAX_ITEM:
|
||||
BLI_assert(false);
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -198,7 +198,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
|
|||
* anyway... */
|
||||
const int cb_flag = ((parent->collection != NULL &&
|
||||
(parent->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ?
|
||||
IDWALK_CB_EMBEDDED :
|
||||
IDWALK_CB_EMBEDDED_NOT_OWNING :
|
||||
IDWALK_CB_NOP);
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
|
||||
data, parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK | cb_flag);
|
||||
|
|
|
@ -237,7 +237,8 @@ struct ResultOffsets {
|
|||
Array<int> profile_indices;
|
||||
|
||||
/** Whether any curve in the profile or curve input has only a single evaluated point. */
|
||||
bool any_single_point_curve;
|
||||
bool any_single_point_main;
|
||||
bool any_single_point_profile;
|
||||
};
|
||||
static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool fill_caps)
|
||||
{
|
||||
|
@ -315,10 +316,8 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool
|
|||
}
|
||||
}
|
||||
},
|
||||
[&]() {
|
||||
result.any_single_point_curve = offsets_contain_single_point(main_offsets) ||
|
||||
offsets_contain_single_point(profile_offsets);
|
||||
});
|
||||
[&]() { result.any_single_point_main = offsets_contain_single_point(main_offsets); },
|
||||
[&]() { result.any_single_point_profile = offsets_contain_single_point(profile_offsets); });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -765,9 +764,13 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
|
|||
positions.slice(info.vert_range));
|
||||
});
|
||||
|
||||
if (!offsets.any_single_point_curve) {
|
||||
/* If there are no single point curves, every curve combination will always have faces. */
|
||||
mesh->loose_edges_tag_none();
|
||||
if (!offsets.any_single_point_main) {
|
||||
/* If there are no single point curves, every combination will have at least loose edges. */
|
||||
mesh->tag_loose_verts_none();
|
||||
if (!offsets.any_single_point_profile) {
|
||||
/* If there are no single point profiles, every combination will have faces. */
|
||||
mesh->loose_edges_tag_none();
|
||||
}
|
||||
}
|
||||
|
||||
SpanAttributeWriter<bool> sharp_edges;
|
||||
|
|
|
@ -2376,9 +2376,9 @@ class CustomDataLayerImplicitSharing : public ImplicitSharingInfo {
|
|||
};
|
||||
|
||||
/** Create a #ImplicitSharingInfo that takes ownership of the data. */
|
||||
static ImplicitSharingInfo *make_implicit_sharing_info_for_layer(const eCustomDataType type,
|
||||
const void *data,
|
||||
const int totelem)
|
||||
static const ImplicitSharingInfo *make_implicit_sharing_info_for_layer(const eCustomDataType type,
|
||||
const void *data,
|
||||
const int totelem)
|
||||
{
|
||||
return MEM_new<CustomDataLayerImplicitSharing>(__func__, data, totelem, type);
|
||||
}
|
||||
|
|
|
@ -431,7 +431,9 @@ CDataFileLayer *cdf_layer_add(CDataFile *cdf, int type, const char *name, size_t
|
|||
|
||||
/* expand array */
|
||||
newlayer = MEM_calloc_arrayN((cdf->totlayer + 1), sizeof(CDataFileLayer), "CDataFileLayer");
|
||||
memcpy(newlayer, cdf->layer, sizeof(CDataFileLayer) * cdf->totlayer);
|
||||
if (cdf->totlayer > 0) {
|
||||
memcpy(newlayer, cdf->layer, sizeof(CDataFileLayer) * cdf->totlayer);
|
||||
}
|
||||
cdf->layer = newlayer;
|
||||
|
||||
cdf->totlayer++;
|
||||
|
|
|
@ -190,29 +190,27 @@ void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
|
|||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
const Span<int> corner_verts = mesh.corner_verts();
|
||||
|
||||
Array<bool> loose_verts(mesh.totvert, true);
|
||||
|
||||
r_values.fill(true);
|
||||
for (const int corner : IndexRange(mesh.totloop)) {
|
||||
const int point_index = corner_verts[corner];
|
||||
|
||||
loose_verts[point_index] = false;
|
||||
if (!old_values[corner]) {
|
||||
r_values[point_index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deselect loose vertices without corners that are still selected from the 'true' default. */
|
||||
/* The record fact says that the value is true.
|
||||
* Writing to the array from different threads is okay because each thread sets the same value.
|
||||
*/
|
||||
threading::parallel_for(loose_verts.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int vert_index : range) {
|
||||
if (loose_verts[vert_index]) {
|
||||
r_values[vert_index] = false;
|
||||
const bke::LooseVertCache &loose_verts = mesh.verts_no_face();
|
||||
if (loose_verts.count > 0) {
|
||||
const BitSpan bits = loose_verts.is_loose_bits;
|
||||
threading::parallel_for(bits.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int vert_index : range) {
|
||||
if (bits[vert_index]) {
|
||||
r_values[vert_index] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static GVArray adapt_mesh_domain_corner_to_point(const Mesh &mesh, const GVArray &varray)
|
||||
|
@ -754,20 +752,26 @@ static bool can_simple_adapt_for_single(const Mesh &mesh,
|
|||
/* All other domains are always connected to points. */
|
||||
return true;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
/* There may be loose vertices not connected to edges. */
|
||||
return ELEM(to_domain, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER);
|
||||
if (to_domain == ATTR_DOMAIN_POINT) {
|
||||
return mesh.loose_verts().count == 0;
|
||||
}
|
||||
return true;
|
||||
case ATTR_DOMAIN_FACE:
|
||||
/* There may be loose vertices or edges not connected to faces. */
|
||||
if (to_domain == ATTR_DOMAIN_POINT) {
|
||||
return mesh.verts_no_face().count == 0;
|
||||
}
|
||||
if (to_domain == ATTR_DOMAIN_EDGE) {
|
||||
return mesh.loose_edges().count == 0;
|
||||
}
|
||||
return to_domain == ATTR_DOMAIN_CORNER;
|
||||
return true;
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
/* Only faces are always connected to corners. */
|
||||
if (to_domain == ATTR_DOMAIN_POINT) {
|
||||
return mesh.verts_no_face().count == 0;
|
||||
}
|
||||
if (to_domain == ATTR_DOMAIN_EDGE) {
|
||||
return mesh.loose_edges().count == 0;
|
||||
}
|
||||
return to_domain == ATTR_DOMAIN_FACE;
|
||||
return true;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
|
|
|
@ -137,7 +137,7 @@ static bool lib_id_library_local_paths_callback(BPathForeachPathData *bpath_data
|
|||
/* Path was relative and is now absolute. Remap.
|
||||
* Important BLI_path_normalize runs before the path is made relative
|
||||
* because it won't work for paths that start with "//../" */
|
||||
BLI_path_normalize(base_new, filepath);
|
||||
BLI_path_normalize(filepath);
|
||||
BLI_path_rel(filepath, base_new);
|
||||
BLI_strncpy(r_path_dst, filepath, FILE_MAX);
|
||||
return true;
|
||||
|
|
|
@ -115,14 +115,14 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
|
|||
/* This is a direct copy of a main mesh, so for now it has the same topology. */
|
||||
mesh_dst->runtime->deformed_only = true;
|
||||
}
|
||||
/* This option is set for run-time meshes that have been copied from the current objects mode.
|
||||
/* This option is set for run-time meshes that have been copied from the current object's mode.
|
||||
* Currently this is used for edit-mesh although it could be used for sculpt or other
|
||||
* kinds of data specific to an objects mode.
|
||||
* kinds of data specific to an object's mode.
|
||||
*
|
||||
* The flag signals that the mesh hasn't been modified from the data that generated it,
|
||||
* allowing us to use the object-mode data for drawing.
|
||||
*
|
||||
* While this could be the callers responsibility, keep here since it's
|
||||
* While this could be the caller's responsibility, keep here since it's
|
||||
* highly unlikely we want to create a duplicate and not use it for drawing. */
|
||||
mesh_dst->runtime->is_original_bmesh = false;
|
||||
|
||||
|
@ -130,6 +130,8 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
|
|||
* when the source is persistent and edits to the destination mesh don't affect the caches.
|
||||
* Caches will be "un-shared" as necessary later on. */
|
||||
mesh_dst->runtime->bounds_cache = mesh_src->runtime->bounds_cache;
|
||||
mesh_dst->runtime->loose_verts_cache = mesh_src->runtime->loose_verts_cache;
|
||||
mesh_dst->runtime->verts_no_face_cache = mesh_src->runtime->verts_no_face_cache;
|
||||
mesh_dst->runtime->loose_edges_cache = mesh_src->runtime->loose_edges_cache;
|
||||
mesh_dst->runtime->looptris_cache = mesh_src->runtime->looptris_cache;
|
||||
|
||||
|
|
|
@ -106,32 +106,90 @@ MeshRuntime::~MeshRuntime()
|
|||
}
|
||||
}
|
||||
|
||||
static int reset_bits_and_count(MutableBitSpan bits, const Span<int> indices_to_reset)
|
||||
{
|
||||
int count = bits.size();
|
||||
for (const int vert : indices_to_reset) {
|
||||
if (bits[vert]) {
|
||||
bits[vert].reset();
|
||||
count--;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void bit_vector_with_reset_bits_or_empty(const Span<int> indices_to_reset,
|
||||
const int indexed_elems_num,
|
||||
BitVector<> &r_bits,
|
||||
int &r_count)
|
||||
{
|
||||
r_bits.resize(0);
|
||||
r_bits.resize(indexed_elems_num, true);
|
||||
r_count = reset_bits_and_count(r_bits, indices_to_reset);
|
||||
if (r_count == 0) {
|
||||
r_bits.clear_and_shrink();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there are no loose edges and no loose vertices, all vertices are used by faces.
|
||||
*/
|
||||
static void try_tag_verts_no_face_none(const Mesh &mesh)
|
||||
{
|
||||
if (!mesh.runtime->loose_edges_cache.is_cached() || mesh.loose_edges().count > 0) {
|
||||
return;
|
||||
}
|
||||
if (!mesh.runtime->loose_verts_cache.is_cached() || mesh.loose_verts().count > 0) {
|
||||
return;
|
||||
}
|
||||
mesh.runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
|
||||
r_data.is_loose_bits.clear_and_shrink();
|
||||
r_data.count = 0;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
const blender::bke::LooseVertCache &Mesh::loose_verts() const
|
||||
{
|
||||
using namespace blender::bke;
|
||||
this->runtime->loose_verts_cache.ensure([&](LooseVertCache &r_data) {
|
||||
const Span<int> verts = this->edges().cast<int>();
|
||||
bit_vector_with_reset_bits_or_empty(verts, this->totvert, r_data.is_loose_bits, r_data.count);
|
||||
});
|
||||
return this->runtime->loose_verts_cache.data();
|
||||
}
|
||||
|
||||
const blender::bke::LooseVertCache &Mesh::verts_no_face() const
|
||||
{
|
||||
using namespace blender::bke;
|
||||
this->runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
|
||||
const Span<int> verts = this->corner_verts();
|
||||
bit_vector_with_reset_bits_or_empty(verts, this->totvert, r_data.is_loose_bits, r_data.count);
|
||||
});
|
||||
return this->runtime->verts_no_face_cache.data();
|
||||
}
|
||||
|
||||
const blender::bke::LooseEdgeCache &Mesh::loose_edges() const
|
||||
{
|
||||
using namespace blender::bke;
|
||||
this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) {
|
||||
blender::BitVector<> &loose_edges = r_data.is_loose_bits;
|
||||
loose_edges.resize(0);
|
||||
loose_edges.resize(this->totedge, true);
|
||||
|
||||
int count = this->totedge;
|
||||
for (const int edge : this->corner_edges()) {
|
||||
if (loose_edges[edge]) {
|
||||
loose_edges[edge].reset();
|
||||
count--;
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
loose_edges.clear_and_shrink();
|
||||
}
|
||||
r_data.count = count;
|
||||
const Span<int> edges = this->corner_edges();
|
||||
bit_vector_with_reset_bits_or_empty(edges, this->totedge, r_data.is_loose_bits, r_data.count);
|
||||
});
|
||||
|
||||
return this->runtime->loose_edges_cache.data();
|
||||
}
|
||||
|
||||
void Mesh::tag_loose_verts_none() const
|
||||
{
|
||||
using namespace blender::bke;
|
||||
this->runtime->loose_verts_cache.ensure([&](LooseVertCache &r_data) {
|
||||
r_data.is_loose_bits.clear_and_shrink();
|
||||
r_data.count = 0;
|
||||
});
|
||||
try_tag_verts_no_face_none(*this);
|
||||
}
|
||||
|
||||
void Mesh::loose_edges_tag_none() const
|
||||
{
|
||||
using namespace blender::bke;
|
||||
|
@ -139,6 +197,7 @@ void Mesh::loose_edges_tag_none() const
|
|||
r_data.is_loose_bits.clear_and_shrink();
|
||||
r_data.count = 0;
|
||||
});
|
||||
try_tag_verts_no_face_none(*this);
|
||||
}
|
||||
|
||||
blender::Span<MLoopTri> Mesh::looptris() const
|
||||
|
@ -219,6 +278,8 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
|
|||
free_subdiv_ccg(*mesh->runtime);
|
||||
mesh->runtime->bounds_cache.tag_dirty();
|
||||
mesh->runtime->loose_edges_cache.tag_dirty();
|
||||
mesh->runtime->loose_verts_cache.tag_dirty();
|
||||
mesh->runtime->verts_no_face_cache.tag_dirty();
|
||||
mesh->runtime->looptris_cache.tag_dirty();
|
||||
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
|
||||
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
|
||||
|
@ -237,6 +298,8 @@ void BKE_mesh_tag_edges_split(struct Mesh *mesh)
|
|||
reset_normals(*mesh->runtime);
|
||||
free_subdiv_ccg(*mesh->runtime);
|
||||
mesh->runtime->loose_edges_cache.tag_dirty();
|
||||
mesh->runtime->loose_verts_cache.tag_dirty();
|
||||
mesh->runtime->verts_no_face_cache.tag_dirty();
|
||||
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
|
||||
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
|
||||
if (mesh->runtime->shrinkwrap_data) {
|
||||
|
|
|
@ -63,20 +63,10 @@ BLI_INLINE void mesh_calc_tessellation_for_face_impl(const Span<int> corner_vert
|
|||
MLoopTri *mlt_a = mlt++;
|
||||
create_tri(0, 2, 3);
|
||||
MLoopTri *mlt_b = mlt;
|
||||
|
||||
if (UNLIKELY(face_normal ? is_quad_flip_v3_first_third_fast_with_normal(
|
||||
/* Simpler calculation (using the normal). */
|
||||
positions[corner_verts[mlt_a->tri[0]]],
|
||||
positions[corner_verts[mlt_a->tri[1]]],
|
||||
positions[corner_verts[mlt_a->tri[2]]],
|
||||
positions[corner_verts[mlt_b->tri[2]]],
|
||||
normal_precalc) :
|
||||
is_quad_flip_v3_first_third_fast(
|
||||
/* Expensive calculation (no normal). */
|
||||
positions[corner_verts[mlt_a->tri[0]]],
|
||||
positions[corner_verts[mlt_a->tri[1]]],
|
||||
positions[corner_verts[mlt_a->tri[2]]],
|
||||
positions[corner_verts[mlt_b->tri[2]]]))) {
|
||||
if (UNLIKELY(is_quad_flip_v3_first_third_fast(positions[corner_verts[mlt_a->tri[0]]],
|
||||
positions[corner_verts[mlt_a->tri[1]]],
|
||||
positions[corner_verts[mlt_a->tri[2]]],
|
||||
positions[corner_verts[mlt_b->tri[2]]]))) {
|
||||
/* Flip out of degenerate 0-2 state. */
|
||||
mlt_a->tri[2] = mlt_b->tri[2];
|
||||
mlt_b->tri[0] = mlt_a->tri[1];
|
||||
|
|
|
@ -795,7 +795,7 @@ static void foreach_vertex_of_loose_edge(const SubdivForeachContext *foreach_con
|
|||
static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context,
|
||||
const int subdiv_v1,
|
||||
const int subdiv_v2,
|
||||
const char crease)
|
||||
const float crease)
|
||||
{
|
||||
/* This is a bit overhead to use atomics in such a simple function called from many threads,
|
||||
* but this allows to save quite measurable amount of memory. */
|
||||
|
@ -805,7 +805,7 @@ static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context,
|
|||
Edge *edge = &reshape_smooth_context->geometry.edges[edge_index];
|
||||
edge->v1 = subdiv_v1;
|
||||
edge->v2 = subdiv_v2;
|
||||
edge->sharpness = BKE_subdiv_crease_to_sharpness_char(crease);
|
||||
edge->sharpness = BKE_subdiv_crease_to_sharpness_f(crease);
|
||||
}
|
||||
|
||||
static void foreach_edge(const SubdivForeachContext *foreach_context,
|
||||
|
@ -821,7 +821,7 @@ static void foreach_edge(const SubdivForeachContext *foreach_context,
|
|||
|
||||
if (reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR) {
|
||||
if (!is_loose) {
|
||||
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, char(255));
|
||||
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, 1.0f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -837,8 +837,8 @@ static void foreach_edge(const SubdivForeachContext *foreach_context,
|
|||
return;
|
||||
}
|
||||
/* Edges without crease are to be ignored as well. */
|
||||
const char crease = get_effective_crease(reshape_smooth_context, coarse_edge_index);
|
||||
if (crease == 0) {
|
||||
const float crease = get_effective_crease(reshape_smooth_context, coarse_edge_index);
|
||||
if (crease == 0.0f) {
|
||||
return;
|
||||
}
|
||||
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, crease);
|
||||
|
|
|
@ -1020,8 +1020,6 @@ static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *conte
|
|||
* so they can be used from #BMesh. */
|
||||
multires_unsubdivide_add_original_index_datalayers(base_mesh);
|
||||
|
||||
const int base_l_layer_index = CustomData_get_named_layer_index(
|
||||
&base_mesh->ldata, CD_PROP_INT32, lname);
|
||||
BMesh *bm_base_mesh = get_bmesh_from_mesh(base_mesh);
|
||||
BMIter iter, iter_a, iter_b;
|
||||
BMVert *v;
|
||||
|
@ -1031,8 +1029,8 @@ static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *conte
|
|||
BM_mesh_elem_table_ensure(bm_base_mesh, BM_FACE);
|
||||
|
||||
/* Get the data-layer that contains the loops indices. */
|
||||
const int base_l_offset = CustomData_get_n_offset(
|
||||
&bm_base_mesh->ldata, CD_PROP_INT32, base_l_layer_index);
|
||||
const int base_l_offset = CustomData_get_offset_named(
|
||||
&bm_base_mesh->ldata, CD_PROP_INT32, lname);
|
||||
|
||||
const blender::OffsetIndices polys = base_mesh->polys();
|
||||
const blender::Span<int> corner_verts = base_mesh->corner_verts();
|
||||
|
|
|
@ -2430,29 +2430,7 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree,
|
|||
return node_dst;
|
||||
}
|
||||
|
||||
static void for_each_node_group_instance(Main &bmain,
|
||||
const bNodeTree &node_group,
|
||||
const Span<int> tree_types_to_lookup,
|
||||
const FunctionRef<void(bNode &)> func)
|
||||
{
|
||||
LISTBASE_FOREACH (bNodeTree *, other_group, &bmain.nodetrees) {
|
||||
if (!tree_types_to_lookup.contains(other_group->type)) {
|
||||
continue;
|
||||
}
|
||||
if (other_group == &node_group) {
|
||||
continue;
|
||||
}
|
||||
|
||||
other_group->ensure_topology_cache();
|
||||
for (bNode *node : other_group->group_nodes()) {
|
||||
if (node->id == &node_group.id) {
|
||||
func(*node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void node_socket_move_default_value(Main &bmain,
|
||||
void node_socket_move_default_value(Main & /*bmain*/,
|
||||
bNodeTree &tree,
|
||||
bNodeSocket &src,
|
||||
bNodeSocket &dst)
|
||||
|
@ -2490,14 +2468,6 @@ void node_socket_move_default_value(Main &bmain,
|
|||
dst_values.append(&dst_node.id);
|
||||
break;
|
||||
}
|
||||
case NODE_GROUP_INPUT: {
|
||||
for_each_node_group_instance(bmain, tree, {NTREE_GEOMETRY}, [&](bNode &node_group) {
|
||||
bNodeSocket &socket = node_group.input_by_identifier(dst.identifier);
|
||||
Image **tmp_dst_value = &socket.default_value_typed<bNodeSocketValueImage>()->value;
|
||||
dst_values.append(reinterpret_cast<ID **>(tmp_dst_value));
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -819,6 +819,15 @@ void BKE_sound_set_scene_sound_pitch(void *handle, float pitch, char animated)
|
|||
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PITCH, sound_cfra, &pitch, animated);
|
||||
}
|
||||
|
||||
void BKE_sound_set_scene_sound_pitch_constant_range(void *handle,
|
||||
int frame_start,
|
||||
int frame_end,
|
||||
float pitch)
|
||||
{
|
||||
AUD_SequenceEntry_setConstantRangeAnimationData(
|
||||
handle, AUD_AP_PITCH, frame_start, frame_end, &pitch);
|
||||
}
|
||||
|
||||
void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated)
|
||||
{
|
||||
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PANNING, sound_cfra, &pan, animated);
|
||||
|
@ -1369,6 +1378,12 @@ void BKE_sound_set_scene_sound_pitch(void *UNUSED(handle),
|
|||
char UNUSED(animated))
|
||||
{
|
||||
}
|
||||
void BKE_sound_set_scene_sound_pitch_constant_range(void *UNUSED(handle),
|
||||
int UNUSED(frame_start),
|
||||
int UNUSED(frame_end),
|
||||
float UNUSED(pitch))
|
||||
{
|
||||
}
|
||||
float BKE_sound_get_length(struct Main *UNUSED(bmain), bSound *UNUSED(sound))
|
||||
{
|
||||
return 0;
|
||||
|
|
|
@ -161,7 +161,7 @@ static float get_edge_sharpness(const OpenSubdiv_Converter *converter, int manif
|
|||
return 10.0f;
|
||||
}
|
||||
#endif
|
||||
if (!storage->settings.use_creases || storage->cd_edge_crease == nullptr) {
|
||||
if (storage->cd_edge_crease == nullptr) {
|
||||
return 0.0f;
|
||||
}
|
||||
const int edge_index = storage->manifold_edge_index_reverse[manifold_edge_index];
|
||||
|
@ -177,6 +177,9 @@ static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter,
|
|||
return true;
|
||||
}
|
||||
#endif
|
||||
if (storage->infinite_sharp_vertices_map == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const int vertex_index = storage->manifold_vertex_index_reverse[manifold_vertex_index];
|
||||
return BLI_BITMAP_TEST_BOOL(storage->infinite_sharp_vertices_map, vertex_index);
|
||||
}
|
||||
|
@ -184,7 +187,7 @@ static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter,
|
|||
static float get_vertex_sharpness(const OpenSubdiv_Converter *converter, int manifold_vertex_index)
|
||||
{
|
||||
ConverterStorage *storage = static_cast<ConverterStorage *>(converter->user_data);
|
||||
if (!storage->settings.use_creases || storage->cd_vertex_crease == nullptr) {
|
||||
if (storage->cd_vertex_crease == nullptr) {
|
||||
return 0.0f;
|
||||
}
|
||||
const int vertex_index = storage->manifold_vertex_index_reverse[manifold_vertex_index];
|
||||
|
@ -211,16 +214,15 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la
|
|||
storage->loop_uv_indices = static_cast<int *>(
|
||||
MEM_malloc_arrayN(mesh->totloop, sizeof(int), "loop uv vertex index"));
|
||||
}
|
||||
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(
|
||||
storage->polys,
|
||||
(const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"),
|
||||
(const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".select_poly"),
|
||||
storage->corner_verts.data(),
|
||||
mloopuv,
|
||||
num_vert,
|
||||
limit,
|
||||
false,
|
||||
true);
|
||||
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(storage->polys,
|
||||
nullptr,
|
||||
nullptr,
|
||||
storage->corner_verts.data(),
|
||||
mloopuv,
|
||||
num_vert,
|
||||
limit,
|
||||
false,
|
||||
true);
|
||||
/* NOTE: First UV vertex is supposed to be always marked as separate. */
|
||||
storage->num_uv_coordinates = -1;
|
||||
for (int vertex_index = 0; vertex_index < num_vert; vertex_index++) {
|
||||
|
@ -264,7 +266,7 @@ static void free_user_data(const OpenSubdiv_Converter *converter)
|
|||
ConverterStorage *user_data = static_cast<ConverterStorage *>(converter->user_data);
|
||||
MEM_SAFE_FREE(user_data->loop_uv_indices);
|
||||
MEM_freeN(user_data->manifold_vertex_index);
|
||||
MEM_freeN(user_data->infinite_sharp_vertices_map);
|
||||
MEM_SAFE_FREE(user_data->infinite_sharp_vertices_map);
|
||||
MEM_freeN(user_data->manifold_vertex_index_reverse);
|
||||
MEM_freeN(user_data->manifold_edge_index_reverse);
|
||||
MEM_freeN(user_data);
|
||||
|
@ -306,7 +308,7 @@ static void init_functions(OpenSubdiv_Converter *converter)
|
|||
converter->freeUserData = free_user_data;
|
||||
}
|
||||
|
||||
static void initialize_manifold_index_array(const BLI_bitmap *used_map,
|
||||
static void initialize_manifold_index_array(const blender::BitSpan not_used_map,
|
||||
const int num_elements,
|
||||
int **r_indices,
|
||||
int **r_indices_reverse,
|
||||
|
@ -323,7 +325,7 @@ static void initialize_manifold_index_array(const BLI_bitmap *used_map,
|
|||
}
|
||||
int offset = 0;
|
||||
for (int i = 0; i < num_elements; i++) {
|
||||
if (BLI_BITMAP_TEST_BOOL(used_map, i)) {
|
||||
if (not_used_map.is_empty() || !not_used_map[i]) {
|
||||
if (indices != nullptr) {
|
||||
indices[i] = i - offset;
|
||||
}
|
||||
|
@ -349,42 +351,35 @@ static void initialize_manifold_index_array(const BLI_bitmap *used_map,
|
|||
|
||||
static void initialize_manifold_indices(ConverterStorage *storage)
|
||||
{
|
||||
using namespace blender;
|
||||
const Mesh *mesh = storage->mesh;
|
||||
const blender::Span<blender::int2> edges = storage->edges;
|
||||
const blender::OffsetIndices<int> polys = storage->polys;
|
||||
const blender::Span<int> corner_verts = storage->corner_verts;
|
||||
const blender::Span<int> corner_edges = storage->corner_edges;
|
||||
/* Set bits of elements which are not loose. */
|
||||
BLI_bitmap *vert_used_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map");
|
||||
BLI_bitmap *edge_used_map = BLI_BITMAP_NEW(mesh->totedge, "edge used map");
|
||||
for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) {
|
||||
for (const int corner : polys[poly_index]) {
|
||||
BLI_BITMAP_ENABLE(vert_used_map, corner_verts[corner]);
|
||||
BLI_BITMAP_ENABLE(edge_used_map, corner_edges[corner]);
|
||||
}
|
||||
}
|
||||
initialize_manifold_index_array(vert_used_map,
|
||||
const bke::LooseVertCache &loose_verts = mesh->verts_no_face();
|
||||
const bke::LooseEdgeCache &loose_edges = mesh->loose_edges();
|
||||
initialize_manifold_index_array(loose_verts.is_loose_bits,
|
||||
mesh->totvert,
|
||||
&storage->manifold_vertex_index,
|
||||
&storage->manifold_vertex_index_reverse,
|
||||
&storage->num_manifold_vertices);
|
||||
initialize_manifold_index_array(edge_used_map,
|
||||
initialize_manifold_index_array(loose_edges.is_loose_bits,
|
||||
mesh->totedge,
|
||||
nullptr,
|
||||
&storage->manifold_edge_index_reverse,
|
||||
&storage->num_manifold_edges);
|
||||
/* Initialize infinite sharp mapping. */
|
||||
storage->infinite_sharp_vertices_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map");
|
||||
for (int edge_index = 0; edge_index < mesh->totedge; edge_index++) {
|
||||
if (!BLI_BITMAP_TEST_BOOL(edge_used_map, edge_index)) {
|
||||
const blender::int2 &edge = edges[edge_index];
|
||||
BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge[0]);
|
||||
BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge[1]);
|
||||
if (loose_edges.count > 0) {
|
||||
const Span<int2> edges = storage->edges;
|
||||
storage->infinite_sharp_vertices_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map");
|
||||
for (int edge_index = 0; edge_index < mesh->totedge; edge_index++) {
|
||||
if (loose_edges.is_loose_bits[edge_index]) {
|
||||
const int2 edge = edges[edge_index];
|
||||
BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge[0]);
|
||||
BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Free working variables. */
|
||||
MEM_freeN(vert_used_map);
|
||||
MEM_freeN(edge_used_map);
|
||||
else {
|
||||
storage->infinite_sharp_vertices_map = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void init_user_data(OpenSubdiv_Converter *converter,
|
||||
|
@ -399,10 +394,12 @@ static void init_user_data(OpenSubdiv_Converter *converter,
|
|||
user_data->polys = mesh->polys();
|
||||
user_data->corner_verts = mesh->corner_verts();
|
||||
user_data->corner_edges = mesh->corner_edges();
|
||||
user_data->cd_vertex_crease = static_cast<const float *>(
|
||||
CustomData_get_layer(&mesh->vdata, CD_CREASE));
|
||||
user_data->cd_edge_crease = static_cast<const float *>(
|
||||
CustomData_get_layer(&mesh->edata, CD_CREASE));
|
||||
if (settings->use_creases) {
|
||||
user_data->cd_vertex_crease = static_cast<const float *>(
|
||||
CustomData_get_layer(&mesh->vdata, CD_CREASE));
|
||||
user_data->cd_edge_crease = static_cast<const float *>(
|
||||
CustomData_get_layer(&mesh->edata, CD_CREASE));
|
||||
}
|
||||
user_data->loop_uv_indices = nullptr;
|
||||
initialize_manifold_indices(user_data);
|
||||
converter->user_data = user_data;
|
||||
|
|
|
@ -91,9 +91,3 @@ BLI_INLINE float BKE_subdiv_crease_to_sharpness_f(float edge_crease)
|
|||
{
|
||||
return edge_crease * edge_crease * 10.0f;
|
||||
}
|
||||
|
||||
BLI_INLINE float BKE_subdiv_crease_to_sharpness_char(char edge_crease)
|
||||
{
|
||||
const float edge_crease_f = edge_crease / 255.0f;
|
||||
return BKE_subdiv_crease_to_sharpness_f(edge_crease_f);
|
||||
}
|
||||
|
|
|
@ -1192,11 +1192,19 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
|
|||
BitVector<> &bit_vector = result->runtime->subsurf_optimal_display_edges;
|
||||
bit_vector.clear();
|
||||
bit_vector.resize(subdiv_context.subdiv_display_edges.size());
|
||||
threading::parallel_for_aligned(span.index_range(), 4096, 64, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
bit_vector[i].set(span[i]);
|
||||
}
|
||||
});
|
||||
threading::parallel_for_aligned(
|
||||
span.index_range(), 4096, bits::BitsPerInt, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
bit_vector[i].set(span[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (coarse_mesh->verts_no_face().count == 0) {
|
||||
result->tag_loose_verts_none();
|
||||
}
|
||||
if (coarse_mesh->loose_edges().count == 0) {
|
||||
result->loose_edges_tag_none();
|
||||
}
|
||||
|
||||
if (subdiv->settings.is_simple) {
|
||||
|
|
|
@ -170,12 +170,9 @@ Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(SubsurfRuntimeData *runtim
|
|||
return runtime_data->subdiv_gpu = BKE_subdiv_update_from_mesh(
|
||||
runtime_data->subdiv_gpu, &runtime_data->settings, mesh);
|
||||
}
|
||||
else {
|
||||
runtime_data->used_cpu = 2;
|
||||
|
||||
return runtime_data->subdiv_cpu = BKE_subdiv_update_from_mesh(
|
||||
runtime_data->subdiv_cpu, &runtime_data->settings, mesh);
|
||||
}
|
||||
runtime_data->used_cpu = 2;
|
||||
return runtime_data->subdiv_cpu = BKE_subdiv_update_from_mesh(
|
||||
runtime_data->subdiv_cpu, &runtime_data->settings, mesh);
|
||||
}
|
||||
|
||||
int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode)
|
||||
|
|
|
@ -625,7 +625,7 @@ fn::GField DataTypeConversions::try_convert(fn::GField field, const CPPType &to_
|
|||
}
|
||||
const mf::MultiFunction &fn = *this->get_conversion_multi_function(
|
||||
mf::DataType::ForSingle(from_type), mf::DataType::ForSingle(to_type));
|
||||
return {std::make_shared<fn::FieldOperation>(fn, Vector<fn::GField>{std::move(field)})};
|
||||
return {fn::FieldOperation::Create(fn, {std::move(field)})};
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -734,6 +734,25 @@ typedef struct VFontToCurveIter {
|
|||
int status;
|
||||
} VFontToCurveIter;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VFont Mouse Cursor to Text Offset
|
||||
*
|
||||
* This is an optional argument to `vfont_to_curve` for getting the text
|
||||
* offset into the string at a mouse cursor location. Used for getting
|
||||
* text cursor (caret) position or selection range.
|
||||
* \{ */
|
||||
/* Used when translating a mouse cursor location to a position within the string. */
|
||||
typedef struct VFontCursor_Params {
|
||||
/* Mouse cursor location in Object coordinate space as input. */
|
||||
float cursor_location[2];
|
||||
/* Character position within EditFont::textbuf as output. */
|
||||
int r_string_offset;
|
||||
} VFontCursor_Params;
|
||||
|
||||
/** \} */
|
||||
|
||||
enum {
|
||||
VFONT_TO_CURVE_INIT = 0,
|
||||
VFONT_TO_CURVE_BISECT,
|
||||
|
@ -774,6 +793,7 @@ static bool vfont_to_curve(Object *ob,
|
|||
Curve *cu,
|
||||
int mode,
|
||||
VFontToCurveIter *iter_data,
|
||||
struct VFontCursor_Params *cursor_params,
|
||||
ListBase *r_nubase,
|
||||
const char32_t **r_text,
|
||||
int *r_text_len,
|
||||
|
@ -1441,6 +1461,35 @@ static bool vfont_to_curve(Object *ob,
|
|||
}
|
||||
}
|
||||
|
||||
if (cursor_params) {
|
||||
cursor_params->r_string_offset = -1;
|
||||
for (i = 0; i <= slen; i++, ct++) {
|
||||
info = &custrinfo[i];
|
||||
ascii = mem[i];
|
||||
if (info->flag & CU_CHINFO_SMALLCAPS_CHECK) {
|
||||
ascii = towupper(ascii);
|
||||
}
|
||||
ct = &chartransdata[i];
|
||||
che = find_vfont_char(vfd, ascii);
|
||||
float charwidth = char_width(cu, che, info);
|
||||
float charhalf = (charwidth / 2.0f);
|
||||
if (cursor_params->cursor_location[1] >= ct->yof - (0.25f * linedist) &&
|
||||
cursor_params->cursor_location[1] <= (ct->yof + (0.75f * linedist))) {
|
||||
/* On this row. */
|
||||
if (cursor_params->cursor_location[0] >= (ct->xof) &&
|
||||
cursor_params->cursor_location[0] <= (ct->xof + charhalf)) {
|
||||
/* Left half of character. */
|
||||
cursor_params->r_string_offset = i;
|
||||
}
|
||||
else if (cursor_params->cursor_location[0] >= (ct->xof + charhalf) &&
|
||||
cursor_params->cursor_location[0] <= (ct->xof + charwidth)) {
|
||||
/* Right half of character. */
|
||||
cursor_params->r_string_offset = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ELEM(mode, FO_CURSUP, FO_CURSDOWN, FO_PAGEUP, FO_PAGEDOWN) &&
|
||||
iter_data->status == VFONT_TO_CURVE_INIT) {
|
||||
ct = &chartransdata[ef->pos];
|
||||
|
@ -1738,13 +1787,49 @@ bool BKE_vfont_to_curve_ex(Object *ob,
|
|||
};
|
||||
|
||||
do {
|
||||
data.ok &= vfont_to_curve(
|
||||
ob, cu, mode, &data, r_nubase, r_text, r_text_len, r_text_free, r_chartransdata);
|
||||
data.ok &= vfont_to_curve(ob,
|
||||
cu,
|
||||
mode,
|
||||
&data,
|
||||
NULL,
|
||||
r_nubase,
|
||||
r_text,
|
||||
r_text_len,
|
||||
r_text_free,
|
||||
r_chartransdata);
|
||||
} while (data.ok && ELEM(data.status, VFONT_TO_CURVE_SCALE_ONCE, VFONT_TO_CURVE_BISECT));
|
||||
|
||||
return data.ok;
|
||||
}
|
||||
|
||||
int BKE_vfont_cursor_to_text_index(Object *ob, float cursor_location[2])
|
||||
{
|
||||
Curve *cu = (Curve *)ob->data;
|
||||
ListBase *r_nubase = &cu->nurb;
|
||||
|
||||
/* TODO: iterating to calculate the scale can be avoided. */
|
||||
|
||||
VFontToCurveIter data = {
|
||||
.iteraction = cu->totbox * FONT_TO_CURVE_SCALE_ITERATIONS,
|
||||
.scale_to_fit = 1.0f,
|
||||
.word_wrap = true,
|
||||
.ok = true,
|
||||
.status = VFONT_TO_CURVE_INIT,
|
||||
};
|
||||
|
||||
VFontCursor_Params cursor_params = {
|
||||
.cursor_location = {cursor_location[0], cursor_location[1]},
|
||||
.r_string_offset = -1,
|
||||
};
|
||||
|
||||
do {
|
||||
data.ok &= vfont_to_curve(
|
||||
ob, cu, FO_CURS, &data, &cursor_params, r_nubase, NULL, NULL, NULL, NULL);
|
||||
} while (data.ok && ELEM(data.status, VFONT_TO_CURVE_SCALE_ONCE, VFONT_TO_CURVE_BISECT));
|
||||
|
||||
return cursor_params.r_string_offset;
|
||||
}
|
||||
|
||||
#undef FONT_TO_CURVE_SCALE_ITERATIONS
|
||||
#undef FONT_TO_CURVE_SCALE_THRESHOLD
|
||||
|
||||
|
|
|
@ -15,6 +15,14 @@ namespace blender::array_utils {
|
|||
* grain-size.
|
||||
*/
|
||||
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size = 4096);
|
||||
template<typename T>
|
||||
inline void copy(const VArray<T> &src, MutableSpan<T> dst, const int64_t grain_size = 4096)
|
||||
{
|
||||
BLI_assert(src.size() == dst.size());
|
||||
threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
|
||||
src.materialize_to_uninitialized(range, dst);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by copying all values from the `src` array. Threaded based on
|
||||
|
@ -28,6 +36,7 @@ inline void copy(const Span<T> src, MutableSpan<T> dst, const int64_t grain_size
|
|||
dst.slice(range).copy_from(src.slice(range));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by copying masked values from the `src` array. Threaded based on
|
||||
* grain-size.
|
||||
|
|
|
@ -217,7 +217,7 @@ template<typename T> struct DefaultHash<T *> {
|
|||
|
||||
template<typename T> uint64_t get_default_hash(const T &v)
|
||||
{
|
||||
return DefaultHash<T>{}(v);
|
||||
return DefaultHash<std::decay_t<T>>{}(v);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2> uint64_t get_default_hash_2(const T1 &v1, const T2 &v2)
|
||||
|
|
|
@ -110,11 +110,11 @@ void *resize_trivial_array_impl(void *old_data,
|
|||
int64_t old_size,
|
||||
int64_t new_size,
|
||||
int64_t alignment,
|
||||
ImplicitSharingInfo **sharing_info);
|
||||
const ImplicitSharingInfo **sharing_info);
|
||||
void *make_trivial_data_mutable_impl(void *old_data,
|
||||
int64_t size,
|
||||
int64_t alignment,
|
||||
ImplicitSharingInfo **sharing_info);
|
||||
const ImplicitSharingInfo **sharing_info);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
@ -124,9 +124,9 @@ void *make_trivial_data_mutable_impl(void *old_data,
|
|||
*/
|
||||
template<typename T>
|
||||
void copy_shared_pointer(T *src_ptr,
|
||||
ImplicitSharingInfo *src_sharing_info,
|
||||
const ImplicitSharingInfo *src_sharing_info,
|
||||
T **r_dst_ptr,
|
||||
ImplicitSharingInfo **r_dst_sharing_info)
|
||||
const ImplicitSharingInfo **r_dst_sharing_info)
|
||||
{
|
||||
*r_dst_ptr = src_ptr;
|
||||
*r_dst_sharing_info = src_sharing_info;
|
||||
|
@ -139,7 +139,7 @@ void copy_shared_pointer(T *src_ptr,
|
|||
/**
|
||||
* Remove this reference to the shared data and remove dangling pointers.
|
||||
*/
|
||||
template<typename T> void free_shared_data(T **data, ImplicitSharingInfo **sharing_info)
|
||||
template<typename T> void free_shared_data(T **data, const ImplicitSharingInfo **sharing_info)
|
||||
{
|
||||
if (*sharing_info) {
|
||||
BLI_assert(*data != nullptr);
|
||||
|
@ -153,13 +153,15 @@ template<typename T> void free_shared_data(T **data, ImplicitSharingInfo **shari
|
|||
* Create an implicit sharing object that takes ownership of the data, allowing it to be shared.
|
||||
* When it is no longer used, the data is freed with #MEM_freeN, so it must be a trivial type.
|
||||
*/
|
||||
ImplicitSharingInfo *info_for_mem_free(void *data);
|
||||
const ImplicitSharingInfo *info_for_mem_free(void *data);
|
||||
|
||||
/**
|
||||
* Make data mutable (single-user) if it is shared. For trivially-copyable data only.
|
||||
*/
|
||||
template<typename T>
|
||||
void make_trivial_data_mutable(T **data, ImplicitSharingInfo **sharing_info, const int64_t size)
|
||||
void make_trivial_data_mutable(T **data,
|
||||
const ImplicitSharingInfo **sharing_info,
|
||||
const int64_t size)
|
||||
{
|
||||
*data = static_cast<T *>(
|
||||
detail::make_trivial_data_mutable_impl(*data, sizeof(T) * size, alignof(T), sharing_info));
|
||||
|
@ -171,7 +173,7 @@ void make_trivial_data_mutable(T **data, ImplicitSharingInfo **sharing_info, con
|
|||
*/
|
||||
template<typename T>
|
||||
void resize_trivial_array(T **data,
|
||||
ImplicitSharingInfo **sharing_info,
|
||||
const ImplicitSharingInfo **sharing_info,
|
||||
int64_t old_size,
|
||||
int64_t new_size)
|
||||
{
|
||||
|
|
|
@ -105,6 +105,23 @@ inline void BLI_kdtree_nd_(range_search_cb_cpp)(const KDTree *tree,
|
|||
},
|
||||
const_cast<Fn *>(&fn));
|
||||
}
|
||||
|
||||
template<typename Fn>
|
||||
inline int BLI_kdtree_nd_(find_nearest_cb_cpp)(const KDTree *tree,
|
||||
const float co[KD_DIMS],
|
||||
KDTreeNearest *r_nearest,
|
||||
Fn &&fn)
|
||||
{
|
||||
return BLI_kdtree_nd_(find_nearest_cb)(
|
||||
tree,
|
||||
co,
|
||||
[](void *user_data, const int index, const float *co, const float dist_sq) {
|
||||
Fn &fn = *static_cast<Fn *>(user_data);
|
||||
return fn(index, co, dist_sq);
|
||||
},
|
||||
&fn,
|
||||
r_nearest);
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef _BLI_CONCAT_AUX
|
||||
|
|
|
@ -145,11 +145,6 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3],
|
|||
const float v2[3],
|
||||
const float v3[3],
|
||||
const float v4[3]);
|
||||
bool is_quad_flip_v3_first_third_fast_with_normal(const float v1[3],
|
||||
const float v2[3],
|
||||
const float v3[3],
|
||||
const float v4[3],
|
||||
const float normal[3]);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -323,19 +323,18 @@ void BLI_path_sequence_encode(
|
|||
/**
|
||||
* Remove redundant characters from \a path and optionally make absolute.
|
||||
*
|
||||
* \param relabase: The path this is relative to, or ignored when NULL.
|
||||
* \param path: Can be any input, and this function converts it to a regular full path.
|
||||
* Also removes garbage from directory paths, like `/../` or double slashes etc.
|
||||
*
|
||||
* \note \a path isn't protected for max string names.
|
||||
*/
|
||||
void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2);
|
||||
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(const char *relabase, char *dir, size_t dir_maxlen) ATTR_NONNULL(2);
|
||||
void BLI_path_normalize_dir(char *dir, size_t dir_maxlen) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Make given name safe to be used in paths.
|
||||
|
|
|
@ -26,7 +26,7 @@ class MEMFreeImplicitSharing : public ImplicitSharingInfo {
|
|||
}
|
||||
};
|
||||
|
||||
ImplicitSharingInfo *info_for_mem_free(void *data)
|
||||
const ImplicitSharingInfo *info_for_mem_free(void *data)
|
||||
{
|
||||
return MEM_new<MEMFreeImplicitSharing>(__func__, data);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace detail {
|
|||
void *make_trivial_data_mutable_impl(void *old_data,
|
||||
const int64_t size,
|
||||
const int64_t alignment,
|
||||
ImplicitSharingInfo **sharing_info)
|
||||
const ImplicitSharingInfo **sharing_info)
|
||||
{
|
||||
if (!old_data) {
|
||||
BLI_assert(size == 0);
|
||||
|
@ -59,7 +59,7 @@ void *resize_trivial_array_impl(void *old_data,
|
|||
const int64_t old_size,
|
||||
const int64_t new_size,
|
||||
const int64_t alignment,
|
||||
ImplicitSharingInfo **sharing_info)
|
||||
const ImplicitSharingInfo **sharing_info)
|
||||
{
|
||||
if (new_size == 0) {
|
||||
if (*sharing_info) {
|
||||
|
@ -79,7 +79,8 @@ void *resize_trivial_array_impl(void *old_data,
|
|||
|
||||
BLI_assert(old_size != 0);
|
||||
if ((*sharing_info)->is_mutable()) {
|
||||
if (auto *info = dynamic_cast<MEMFreeImplicitSharing *>(*sharing_info)) {
|
||||
if (auto *info = const_cast<MEMFreeImplicitSharing *>(
|
||||
dynamic_cast<const MEMFreeImplicitSharing *>(*sharing_info))) {
|
||||
/* If the array was allocated with the MEM allocator, we can use realloc directly, which
|
||||
* could theoretically give better performance if the data can be reused in place. */
|
||||
void *new_data = static_cast<int *>(MEM_reallocN(old_data, new_size));
|
||||
|
|
|
@ -5889,6 +5889,9 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3],
|
|||
const float v3[3],
|
||||
const float v4[3])
|
||||
{
|
||||
/* NOTE: if the faces normal has been calculated it's possible to simplify the following checks,
|
||||
* however this means the solution may be different depending on the existence of normals
|
||||
* causing tessellation to be "unstable" depending on the existence of normals, see #106469. */
|
||||
float d_12[3], d_13[3], d_14[3];
|
||||
float cross_a[3], cross_b[3];
|
||||
sub_v3_v3v3(d_12, v2, v1);
|
||||
|
@ -5899,19 +5902,6 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3],
|
|||
return dot_v3v3(cross_a, cross_b) > 0.0f;
|
||||
}
|
||||
|
||||
bool is_quad_flip_v3_first_third_fast_with_normal(const float v1[3],
|
||||
const float v2[3],
|
||||
const float v3[3],
|
||||
const float v4[3],
|
||||
const float normal[3])
|
||||
{
|
||||
float dir_v3v1[3], tangent[3];
|
||||
sub_v3_v3v3(dir_v3v1, v3, v1);
|
||||
cross_v3_v3v3(tangent, dir_v3v1, normal);
|
||||
const float dot = dot_v3v3(v1, tangent);
|
||||
return (dot_v3v3(v4, tangent) >= dot) || (dot_v3v3(v2, tangent) <= dot);
|
||||
}
|
||||
|
||||
float cubic_tangent_factor_circle_v3(const float tan_l[3], const float tan_r[3])
|
||||
{
|
||||
BLI_ASSERT_UNIT_V3(tan_l);
|
||||
|
|
|
@ -2728,7 +2728,7 @@ void svd_m4(float U[4][4], float s[4], float V[4][4], float A_[4][4])
|
|||
e[k + 1] += 1.0f;
|
||||
}
|
||||
e[k] = -e[k];
|
||||
if ((k + 1 < m) & (e[k] != 0.0f)) {
|
||||
if ((k + 1 < m) && (e[k] != 0.0f)) {
|
||||
float invek1;
|
||||
|
||||
/* Apply the transformation. */
|
||||
|
@ -2812,7 +2812,7 @@ void svd_m4(float U[4][4], float s[4], float V[4][4], float A_[4][4])
|
|||
/* If required, generate V. */
|
||||
|
||||
for (k = n - 1; k >= 0; k--) {
|
||||
if ((k < nrt) & (e[k] != 0.0f)) {
|
||||
if ((k < nrt) && (e[k] != 0.0f)) {
|
||||
for (j = k + 1; j < nu; j++) {
|
||||
float t = 0;
|
||||
for (i = k + 1; i < n; i++) {
|
||||
|
|
|
@ -1962,17 +1962,19 @@ static Face *cdt_tri_as_imesh_face(
|
|||
return facep;
|
||||
}
|
||||
|
||||
/* Like BLI_math's is_quad_flip_v3_first_third_fast_with_normal, with const double3's. */
|
||||
/* Like BLI_math's is_quad_flip_v3_first_third_fast, with const double3's. */
|
||||
static bool is_quad_flip_first_third(const double3 &v1,
|
||||
const double3 &v2,
|
||||
const double3 &v3,
|
||||
const double3 &v4,
|
||||
const double3 &normal)
|
||||
const double3 &v4)
|
||||
{
|
||||
double3 dir_v3v1 = v3 - v1;
|
||||
double3 tangent = math::cross(dir_v3v1, normal);
|
||||
double dot = math::dot(v1, tangent);
|
||||
return (math::dot(v4, tangent) >= dot) || (math::dot(v2, tangent) <= dot);
|
||||
const double3 d_12 = v2 - v1;
|
||||
const double3 d_13 = v3 - v1;
|
||||
const double3 d_14 = v4 - v1;
|
||||
|
||||
const double3 cross_a = math::cross(d_12, d_13);
|
||||
const double3 cross_b = math::cross(d_14, d_13);
|
||||
return math::dot(cross_a, cross_b) > 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2008,7 +2010,7 @@ static Array<Face *> polyfill_triangulate_poly(Face *f, IMeshArena *arena)
|
|||
int eo_23 = f->edge_orig[2];
|
||||
int eo_30 = f->edge_orig[3];
|
||||
Face *f0, *f1;
|
||||
if (UNLIKELY(is_quad_flip_first_third(v0->co, v1->co, v2->co, v3->co, f->plane->norm))) {
|
||||
if (UNLIKELY(is_quad_flip_first_third(v0->co, v1->co, v2->co, v3->co))) {
|
||||
f0 = arena->add_face({v0, v1, v3}, f->orig, {eo_01, -1, eo_30}, {false, false, false});
|
||||
f1 = arena->add_face({v1, v2, v3}, f->orig, {eo_12, eo_23, -1}, {false, false, false});
|
||||
}
|
||||
|
|
|
@ -37,22 +37,16 @@
|
|||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
/* Declarations */
|
||||
/* Declarations. */
|
||||
|
||||
static int BLI_path_unc_prefix_len(const char *path);
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
/**
|
||||
* Return true if the path is absolute ie starts with a drive specifier
|
||||
* (eg A:\) or is a UNC path.
|
||||
*/
|
||||
static bool BLI_path_is_abs(const char *name);
|
||||
|
||||
static bool BLI_path_is_abs_win32(const char *name);
|
||||
#endif /* WIN32 */
|
||||
|
||||
// #define DEBUG_STRSIZE
|
||||
|
||||
/* implementation */
|
||||
|
||||
int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_digits_len)
|
||||
{
|
||||
uint nums = 0, nume = 0;
|
||||
|
@ -102,9 +96,8 @@ int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort
|
|||
strcpy(tail, string + name_end);
|
||||
}
|
||||
if (head) {
|
||||
/* name_end points to last character of head,
|
||||
* make it +1 so null-terminator is nicely placed
|
||||
*/
|
||||
/* Name_end points to last character of head,
|
||||
* make it +1 so null-terminator is nicely placed. */
|
||||
BLI_strncpy(head, string, name_end + 1);
|
||||
}
|
||||
if (r_digits_len) {
|
||||
|
@ -119,84 +112,139 @@ void BLI_path_sequence_encode(
|
|||
BLI_sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
|
||||
}
|
||||
|
||||
static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */
|
||||
|
||||
void BLI_path_normalize(const char *relabase, char *path)
|
||||
void BLI_path_normalize(char *path)
|
||||
{
|
||||
const char *path_orig = path;
|
||||
int path_len;
|
||||
|
||||
ptrdiff_t a;
|
||||
char *start, *eind;
|
||||
if (relabase) {
|
||||
BLI_path_abs(path, relabase);
|
||||
}
|
||||
else {
|
||||
if (path[0] == '/' && path[1] == '/') {
|
||||
if (path[2] == '\0') {
|
||||
return; /* path is "//" - can't clean it */
|
||||
}
|
||||
path = path + 2; /* leave the initial "//" untouched */
|
||||
}
|
||||
}
|
||||
|
||||
/* Note
|
||||
* memmove(start, eind, strlen(eind) + 1);
|
||||
* is the same as
|
||||
* strcpy(start, eind);
|
||||
* except strcpy should not be used because there is overlap,
|
||||
* so use memmove's slightly more obscure syntax - Campbell
|
||||
*/
|
||||
path_len = strlen(path);
|
||||
|
||||
if (path[0] == '/' && path[1] == '/') {
|
||||
path = path + 2; /* Leave the initial `//` untouched. */
|
||||
path_len -= 2;
|
||||
|
||||
/* Strip leading slashes, as they will interfere with the absolute/relative check
|
||||
* (besides being redundant). */
|
||||
int i = 0;
|
||||
while (path[i] == SEP) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i != 0) {
|
||||
memmove(path, path + i, (path_len - i) + 1);
|
||||
path_len -= i;
|
||||
}
|
||||
BLI_assert(path_len == strlen(path));
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
while ((start = strstr(path, "\\.\\"))) {
|
||||
eind = start + strlen("\\.\\") - 1;
|
||||
memmove(start, eind, strlen(eind) + 1);
|
||||
/* Skip to the first slash of the drive or UNC path,
|
||||
* so additional slashes are treated as doubles. */
|
||||
if (path_orig == path) {
|
||||
int path_unc_len = BLI_path_unc_prefix_len(path);
|
||||
if (path_unc_len) {
|
||||
path_unc_len -= 1;
|
||||
BLI_assert(path_unc_len > 0 && path[path_unc_len] == SEP);
|
||||
path += path_unc_len;
|
||||
path_len -= path_unc_len;
|
||||
}
|
||||
else if (isalpha(path[0]) && (path[1] == ':')) {
|
||||
path += 2;
|
||||
path_len -= 2;
|
||||
}
|
||||
}
|
||||
#endif /* WIN32 */
|
||||
|
||||
/* remove two consecutive backslashes, but skip the UNC prefix,
|
||||
* which needs to be preserved */
|
||||
while ((start = strstr(path + BLI_path_unc_prefix_len(path), "\\\\"))) {
|
||||
eind = start + strlen("\\\\") - 1;
|
||||
memmove(start, eind, strlen(eind) + 1);
|
||||
}
|
||||
/* Works on WIN32 as well, because the drive component is skipped. */
|
||||
const bool is_relative = path[0] && (path[0] != SEP);
|
||||
|
||||
while ((start = strstr(path, "\\..\\"))) {
|
||||
eind = start + strlen("\\..\\") - 1;
|
||||
a = start - path - 1;
|
||||
while (a > 0) {
|
||||
if (path[a] == '\\') {
|
||||
break;
|
||||
/* NOTE(@ideasman42):
|
||||
* `memmove(start, eind, strlen(eind) + 1);`
|
||||
* is the same as
|
||||
* `strcpy(start, eind);`
|
||||
* except `strcpy` should not be used because there is overlap,
|
||||
* so use `memmove` 's slightly more obscure syntax. */
|
||||
|
||||
/* Inline replacement:
|
||||
* - `/./` -> `/`.
|
||||
* - `//` -> `/`.
|
||||
* Performed until no more replacements can be made. */
|
||||
if (path_len > 1) {
|
||||
for (int i = path_len - 1; i > 0; i--) {
|
||||
/* Calculate the redundant slash span (if any). */
|
||||
if (path[i] == SEP) {
|
||||
const int i_end = i;
|
||||
do {
|
||||
/* Stepping over elements assumes 'i' references a separator. */
|
||||
BLI_assert(path[i] == SEP);
|
||||
if (path[i - 1] == SEP) {
|
||||
i -= 1; /* Found `//`, replace with `/`. */
|
||||
}
|
||||
else if (i >= 2 && path[i - 1] == '.' && path[i - 2] == SEP) {
|
||||
i -= 2; /* Found `/./`, replace with `/`. */
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (i > 0);
|
||||
|
||||
if (i < i_end) {
|
||||
memmove(path + i, path + i_end, (path_len - i_end) + 1);
|
||||
path_len -= i_end - i;
|
||||
BLI_assert(strlen(path) == path_len);
|
||||
}
|
||||
}
|
||||
a--;
|
||||
}
|
||||
if (a < 0) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
memmove(path + a, eind, strlen(eind) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
while ((start = strstr(path, "/./"))) {
|
||||
eind = start + (3 - 1) /* strlen("/./") - 1 */;
|
||||
memmove(start, eind, strlen(eind) + 1);
|
||||
/* Remove redundant `./` prefix, while it could be kept, it confuses the loop below. */
|
||||
if (is_relative) {
|
||||
if ((path_len > 2) && (path[0] == '.') && (path[1] == SEP)) {
|
||||
memmove(path, path + 2, (path_len - 2) + 1);
|
||||
path_len -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
while ((start = strstr(path, "//"))) {
|
||||
eind = start + (2 - 1) /* strlen("//") - 1 */;
|
||||
memmove(start, eind, strlen(eind) + 1);
|
||||
}
|
||||
const ptrdiff_t a_start = is_relative ? 0 : 1;
|
||||
start = path;
|
||||
while ((start = strstr(start, SEP_STR ".."))) {
|
||||
if (!ELEM(start[3], SEP, '\0')) {
|
||||
start += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((start = strstr(path, "/../"))) {
|
||||
a = start - path - 1;
|
||||
if (a > 0) {
|
||||
/* <prefix>/<parent>/../<postfix> => <prefix>/<postfix> */
|
||||
eind = start + (4 - 1) /* strlen("/../") - 1 */; /* strip "/.." and keep last "/" */
|
||||
while (a > 0 && path[a] != '/') { /* find start of <parent> */
|
||||
a = (start - path) - 1;
|
||||
if (a >= a_start) {
|
||||
/* `<prefix>/<parent>/../<postfix> => <prefix>/<postfix>`. */
|
||||
eind = start + (4 - 1) /* `strlen("/../") - 1` */; /* Strip "/.." and keep the char after. */
|
||||
while (a > 0 && path[a] != SEP) { /* Find start of `<parent>`. */
|
||||
a--;
|
||||
}
|
||||
memmove(path + a, eind, strlen(eind) + 1);
|
||||
|
||||
if (is_relative && (a == 0) && *eind) {
|
||||
/* When the path does not start with a slash, don't copy the first `/` to the destination
|
||||
* as it will make a relative path into an absolute path. */
|
||||
eind += 1;
|
||||
}
|
||||
const size_t eind_len = path_len - (eind - path);
|
||||
BLI_assert(eind_len == strlen(eind));
|
||||
|
||||
/* Only remove the parent if it's not also a `..`. */
|
||||
if (is_relative && STRPREFIX(path + ((path[a] == SEP) ? a + 1 : a), ".." SEP_STR)) {
|
||||
start += 3 /* `strlen("/..")` */;
|
||||
}
|
||||
else {
|
||||
start = path + a;
|
||||
BLI_assert(start < eind);
|
||||
memmove(start, eind, eind_len + 1);
|
||||
path_len -= (eind - start);
|
||||
BLI_assert(strlen(path) == path_len);
|
||||
BLI_assert(!is_relative || (path[0] != SEP));
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Support for odd paths: eg `/../home/me` --> `/home/me`
|
||||
|
@ -208,21 +256,32 @@ void BLI_path_normalize(const char *relabase, char *path)
|
|||
* which meant that the `/../home/me` example actually became `home/me`.
|
||||
* Using offset of 3 gives behavior consistent with the aforementioned
|
||||
* Python routine. */
|
||||
memmove(path, path + 3, strlen(path + 3) + 1);
|
||||
eind = start + 3;
|
||||
const size_t eind_len = path_len - (eind - path);
|
||||
memmove(start, eind, eind_len + 1);
|
||||
path_len -= 3;
|
||||
BLI_assert(strlen(path) == path_len);
|
||||
BLI_assert(!is_relative || (path[0] != SEP));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
if (is_relative && path_len == 0 && (path == path_orig)) {
|
||||
path[0] = '.';
|
||||
path[1] = '\0';
|
||||
path_len += 1;
|
||||
}
|
||||
|
||||
BLI_assert(strlen(path) == path_len);
|
||||
}
|
||||
|
||||
void BLI_path_normalize_dir(const char *relabase, char *dir, size_t dir_maxlen)
|
||||
void BLI_path_normalize_dir(char *dir, size_t dir_maxlen)
|
||||
{
|
||||
/* Would just create an unexpected "/" path, just early exit entirely. */
|
||||
if (dir[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_path_normalize(relabase, dir);
|
||||
BLI_path_normalize(dir);
|
||||
BLI_path_slash_ensure(dir, dir_maxlen);
|
||||
}
|
||||
|
||||
|
@ -253,7 +312,7 @@ bool BLI_filename_make_safe_ex(char *fname, bool allow_tokens)
|
|||
|
||||
/* Forbid only dots. */
|
||||
for (fn = fname; *fn == '.'; fn++) {
|
||||
/* pass */
|
||||
/* Pass. */
|
||||
}
|
||||
if (*fn == '\0') {
|
||||
*fname = '_';
|
||||
|
@ -279,15 +338,14 @@ bool BLI_filename_make_safe_ex(char *fname, bool allow_tokens)
|
|||
|
||||
/* Check for forbidden names - not we have to check all combination
|
||||
* of upper and lower cases, hence the usage of lower_fname
|
||||
* (more efficient than using BLI_strcasestr repeatedly). */
|
||||
* (more efficient than using #BLI_strcasestr repeatedly). */
|
||||
BLI_str_tolower_ascii(lower_fname, len);
|
||||
for (iname = invalid_names; *iname; iname++) {
|
||||
if (strstr(lower_fname, *iname) == lower_fname) {
|
||||
const size_t iname_len = strlen(*iname);
|
||||
/* Only invalid if the whole name is made of the invalid chunk, or it has an
|
||||
* (assumed extension) dot just after. This means it will also catch 'valid'
|
||||
* names like 'aux.foo.bar', but should be
|
||||
* good enough for us! */
|
||||
* (assumed extension) dot just after. This means it will also catch *valid*
|
||||
* names like `aux.foo.bar`, but should be good enough for us! */
|
||||
if ((iname_len == len) || (lower_fname[iname_len] == '.')) {
|
||||
*fname = '_';
|
||||
changed = true;
|
||||
|
@ -311,14 +369,14 @@ bool BLI_filename_make_safe(char *fname)
|
|||
bool BLI_path_make_safe(char *path)
|
||||
{
|
||||
/* Simply apply #BLI_filename_make_safe() over each component of the path.
|
||||
* Luckily enough, same 'safe' rules applies to file & directory names. */
|
||||
* Luckily enough, same *safe* rules applies to file & directory names. */
|
||||
char *curr_slash, *curr_path = path;
|
||||
bool changed = false;
|
||||
bool skip_first = false;
|
||||
|
||||
#ifdef WIN32
|
||||
if (BLI_path_is_abs(path)) {
|
||||
/* Do not make safe 'C:' in 'C:\foo\bar'... */
|
||||
if (BLI_path_is_abs_win32(path)) {
|
||||
/* Do not make safe `C:` in `C:\foo\bar`. */
|
||||
skip_first = true;
|
||||
}
|
||||
#endif
|
||||
|
@ -361,7 +419,7 @@ static int BLI_path_unc_prefix_len(const char *path)
|
|||
{
|
||||
if (BLI_path_is_unc(path)) {
|
||||
if ((path[2] == '?') && (path[3] == '\\')) {
|
||||
/* we assume long UNC path like \\?\server\share\folder etc... */
|
||||
/* We assume long UNC path like `\\?\server\share\folder` etc. */
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
@ -374,10 +432,14 @@ static int BLI_path_unc_prefix_len(const char *path)
|
|||
#if defined(WIN32)
|
||||
|
||||
/**
|
||||
* Return true if the path is absolute ie starts with a drive specifier
|
||||
* (eg A:\) or is a UNC path.
|
||||
* Return true if the path is an absolute path on a WIN32 file-system, it either:
|
||||
* - Starts with a drive specifier* (eg `A:\`).
|
||||
* - Is a UNC path.
|
||||
*
|
||||
* \note Not to be confused with the opposite of #BLI_path_is_rel which checks for the
|
||||
* Blender specific convention of using `//` prefix for blend-file relative paths.
|
||||
*/
|
||||
static bool BLI_path_is_abs(const char *name)
|
||||
static bool BLI_path_is_abs_win32(const char *name)
|
||||
{
|
||||
return (name[1] == ':' && ELEM(name[2], '\\', '/')) || BLI_path_is_unc(name);
|
||||
}
|
||||
|
@ -409,9 +471,10 @@ static void BLI_path_unc_to_short(wchar_t *unc)
|
|||
wchar_t tmp[PATH_MAX];
|
||||
|
||||
int len = wcslen(unc);
|
||||
/* convert:
|
||||
* \\?\UNC\server\share\folder\... to \\server\share\folder\...
|
||||
* \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\...
|
||||
/* Convert:
|
||||
* - `\\?\UNC\server\share\folder\...` to `\\server\share\folder\...`
|
||||
* - `\\?\C:\` to `C:\`
|
||||
* - `\\?\C:\folder\...` to `C:\folder\...`
|
||||
*/
|
||||
if ((len > 3) && (unc[0] == L'\\') && (unc[1] == L'\\') && (unc[2] == L'?') &&
|
||||
ELEM(unc[3], L'\\', L'/')) {
|
||||
|
@ -450,18 +513,18 @@ void BLI_path_rel(char *file, const char *relfile)
|
|||
char temp[FILE_MAX];
|
||||
char res[FILE_MAX];
|
||||
|
||||
/* if file is already relative, bail out */
|
||||
/* If file is already relative, bail out. */
|
||||
if (BLI_path_is_rel(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* also bail out if relative path is not set */
|
||||
/* Also bail out if relative path is not set. */
|
||||
if (relfile[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs(relfile)) {
|
||||
if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs_win32(relfile)) {
|
||||
char *ptemp;
|
||||
/* Fix missing volume name in relative base,
|
||||
* can happen with old `recent-files.txt` files. */
|
||||
|
@ -479,12 +542,12 @@ void BLI_path_rel(char *file, const char *relfile)
|
|||
if (BLI_strnlen(file, 3) > 2) {
|
||||
bool is_unc = BLI_path_is_unc(file);
|
||||
|
||||
/* Ensure paths are both UNC paths or are both drives */
|
||||
/* Ensure paths are both UNC paths or are both drives. */
|
||||
if (BLI_path_is_unc(temp) != is_unc) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure both UNC paths are on the same share */
|
||||
/* Ensure both UNC paths are on the same share. */
|
||||
if (is_unc) {
|
||||
int off;
|
||||
int slash = 0;
|
||||
|
@ -509,16 +572,16 @@ void BLI_path_rel(char *file, const char *relfile)
|
|||
BLI_str_replace_char(temp + BLI_path_unc_prefix_len(temp), '\\', '/');
|
||||
BLI_str_replace_char(file + BLI_path_unc_prefix_len(file), '\\', '/');
|
||||
|
||||
/* remove /./ which confuse the following slash counting... */
|
||||
BLI_path_normalize(NULL, file);
|
||||
BLI_path_normalize(NULL, temp);
|
||||
/* Remove `/./` which confuse the following slash counting. */
|
||||
BLI_path_normalize(file);
|
||||
BLI_path_normalize(temp);
|
||||
|
||||
/* the last slash in the file indicates where the path part ends */
|
||||
/* The last slash in the file indicates where the path part ends. */
|
||||
lslash = BLI_path_slash_rfind(temp);
|
||||
|
||||
if (lslash) {
|
||||
/* find the prefix of the filename that is equal for both filenames.
|
||||
* This is replaced by the two slashes at the beginning */
|
||||
/* Find the prefix of the filename that is equal for both filenames.
|
||||
* This is replaced by the two slashes at the beginning. */
|
||||
const char *p = temp;
|
||||
const char *q = file;
|
||||
char *r = res;
|
||||
|
@ -532,16 +595,14 @@ void BLI_path_rel(char *file, const char *relfile)
|
|||
p++;
|
||||
q++;
|
||||
|
||||
/* don't search beyond the end of the string
|
||||
* in the rare case they match */
|
||||
/* Don't search beyond the end of the string in the rare case they match. */
|
||||
if ((*p == '\0') || (*q == '\0')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* we might have passed the slash when the beginning of a dir matches
|
||||
* so we rewind. Only check on the actual filename
|
||||
*/
|
||||
/* We might have passed the slash when the beginning of a dir matches
|
||||
* so we rewind. Only check on the actual filename. */
|
||||
if (*q != '/') {
|
||||
while ((q >= file) && (*q != '/')) {
|
||||
q--;
|
||||
|
@ -557,11 +618,10 @@ void BLI_path_rel(char *file, const char *relfile)
|
|||
|
||||
r += BLI_strcpy_rlen(r, "//");
|
||||
|
||||
/* p now points to the slash that is at the beginning of the part
|
||||
/* `p` now points to the slash that is at the beginning of the part
|
||||
* where the path is different from the relative path.
|
||||
* We count the number of directories we need to go up in the
|
||||
* hierarchy to arrive at the common 'prefix' of the path
|
||||
*/
|
||||
* hierarchy to arrive at the common prefix of the path. */
|
||||
if (p < temp) {
|
||||
p = temp;
|
||||
}
|
||||
|
@ -572,7 +632,7 @@ void BLI_path_rel(char *file, const char *relfile)
|
|||
p++;
|
||||
}
|
||||
|
||||
/* don't copy the slash at the beginning */
|
||||
/* Don't copy the slash at the beginning. */
|
||||
r += BLI_strncpy_rlen(r, q + 1, FILE_MAX - (r - res));
|
||||
|
||||
#ifdef WIN32
|
||||
|
@ -627,7 +687,7 @@ bool BLI_path_parent_dir(char *path)
|
|||
return false;
|
||||
}
|
||||
if (tail_len == 1) {
|
||||
/* Last path is ".", as normalize should remove this, it's safe to assume failure.
|
||||
/* Last path is `.`, as normalize should remove this, it's safe to assume failure.
|
||||
* This happens when the input a single period (possibly with slashes before or after). */
|
||||
if (path[tail_ofs] == '.') {
|
||||
return false;
|
||||
|
@ -646,7 +706,7 @@ bool BLI_path_parent_dir_until_exists(char *dir)
|
|||
|
||||
/* Loop as long as cur path is not a dir, and we can get a parent path. */
|
||||
while ((BLI_access(dir, R_OK) != 0) && (valid_path = BLI_path_parent_dir(dir))) {
|
||||
/* pass */
|
||||
/* Pass. */
|
||||
}
|
||||
return (valid_path && dir[0]);
|
||||
}
|
||||
|
@ -659,11 +719,11 @@ bool BLI_path_parent_dir_until_exists(char *dir)
|
|||
static bool stringframe_chars(const char *path, int *char_start, int *char_end)
|
||||
{
|
||||
uint ch_sta, ch_end, i;
|
||||
/* Insert current frame: file### -> file001 */
|
||||
/* Insert current frame: `file###` -> `file001`. */
|
||||
ch_sta = ch_end = 0;
|
||||
for (i = 0; path[i] != '\0'; i++) {
|
||||
if (ELEM(path[i], '\\', '/')) {
|
||||
ch_end = 0; /* this is a directory name, don't use any hashes we found */
|
||||
ch_end = 0; /* This is a directory name, don't use any hashes we found. */
|
||||
}
|
||||
else if (path[i] == '#') {
|
||||
ch_sta = i;
|
||||
|
@ -671,9 +731,9 @@ static bool stringframe_chars(const char *path, int *char_start, int *char_end)
|
|||
while (path[ch_end] == '#') {
|
||||
ch_end++;
|
||||
}
|
||||
i = ch_end - 1; /* keep searching */
|
||||
i = ch_end - 1; /* Keep searching. */
|
||||
|
||||
/* don't break, there may be a slash after this that invalidates the previous #'s */
|
||||
/* Don't break, there may be a slash after this that invalidates the previous #'s. */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -718,7 +778,7 @@ bool BLI_path_frame(char *path, int frame, int digits)
|
|||
ensure_digits(path, digits);
|
||||
}
|
||||
|
||||
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
|
||||
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* Warning: `ch_end` is the last # +1. */
|
||||
char tmp[FILE_MAX];
|
||||
BLI_snprintf(
|
||||
tmp, sizeof(tmp), "%.*s%.*d%s", ch_sta, path, ch_end - ch_sta, frame, path + ch_end);
|
||||
|
@ -736,7 +796,7 @@ 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 */
|
||||
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* Warning: `ch_end` is the last # +1. */
|
||||
char tmp[FILE_MAX];
|
||||
BLI_snprintf(tmp,
|
||||
sizeof(tmp),
|
||||
|
@ -812,8 +872,8 @@ void BLI_path_frame_strip(char *path, char *r_ext, const size_t ext_maxlen)
|
|||
|
||||
bool BLI_path_frame_check_chars(const char *path)
|
||||
{
|
||||
int ch_sta, ch_end; /* dummy args */
|
||||
return stringframe_chars(path, &ch_sta, &ch_end);
|
||||
int ch_sta_dummy, ch_end_dummy;
|
||||
return stringframe_chars(path, &ch_sta_dummy, &ch_end_dummy);
|
||||
}
|
||||
|
||||
void BLI_path_to_display_name(char *display_name, int maxlen, const char *name)
|
||||
|
@ -860,16 +920,14 @@ bool BLI_path_abs(char *path, const char *basepath)
|
|||
char base[FILE_MAX];
|
||||
#ifdef WIN32
|
||||
|
||||
/* without this: "" --> "C:\" */
|
||||
/* Without this, an empty string converts to: `C:\` */
|
||||
if (*path == '\0') {
|
||||
return wasrelative;
|
||||
}
|
||||
|
||||
/* we are checking here if we have an absolute path that is not in the current
|
||||
* blend file as a lib main - we are basically checking for the case that a
|
||||
* UNIX root '/' is passed.
|
||||
*/
|
||||
if (!wasrelative && !BLI_path_is_abs(path)) {
|
||||
/* We are checking here if we have an absolute path that is not in the current `.blend` file
|
||||
* as a lib main - we are basically checking for the case that a UNIX root `/` is passed. */
|
||||
if (!wasrelative && !BLI_path_is_abs_win32(path)) {
|
||||
char *p = path;
|
||||
BLI_windows_get_default_root_dir(tmp);
|
||||
/* Get rid of the slashes at the beginning of the path. */
|
||||
|
@ -892,7 +950,7 @@ bool BLI_path_abs(char *path, const char *basepath)
|
|||
* `C:\foo.JPG` -> `/c/foo.JPG` */
|
||||
|
||||
if (isalpha(tmp[0]) && (tmp[1] == ':') && ELEM(tmp[2], '\\', '/')) {
|
||||
tmp[1] = tolower(tmp[0]); /* Replace ':' with drive-letter. */
|
||||
tmp[1] = tolower(tmp[0]); /* Replace `:` with drive-letter. */
|
||||
tmp[0] = '/';
|
||||
/* `\` the slash will be converted later. */
|
||||
}
|
||||
|
@ -916,28 +974,28 @@ bool BLI_path_abs(char *path, const char *basepath)
|
|||
const char *lslash;
|
||||
BLI_strncpy(base, basepath, sizeof(base));
|
||||
|
||||
/* file component is ignored, so don't bother with the trailing slash */
|
||||
BLI_path_normalize(NULL, base);
|
||||
/* File component is ignored, so don't bother with the trailing slash. */
|
||||
BLI_path_normalize(base);
|
||||
lslash = BLI_path_slash_rfind(base);
|
||||
BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
|
||||
|
||||
if (lslash) {
|
||||
/* length up to and including last "/" */
|
||||
/* Length up to and including last `/`. */
|
||||
const int baselen = (int)(lslash - base) + 1;
|
||||
/* use path for temp storage here, we copy back over it right away */
|
||||
BLI_strncpy(path, tmp + 2, FILE_MAX); /* strip "//" */
|
||||
/* Use path for temp storage here, we copy back over it right away. */
|
||||
BLI_strncpy(path, tmp + 2, FILE_MAX); /* Strip `//` prefix. */
|
||||
|
||||
memcpy(tmp, base, baselen); /* prefix with base up to last "/" */
|
||||
BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); /* append path after "//" */
|
||||
BLI_strncpy(path, tmp, FILE_MAX); /* return as result */
|
||||
memcpy(tmp, base, baselen); /* Prefix with base up to last `/`. */
|
||||
BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); /* Append path after `//`. */
|
||||
BLI_strncpy(path, tmp, FILE_MAX); /* Return as result. */
|
||||
}
|
||||
else {
|
||||
/* base doesn't seem to be a directory--ignore it and just strip "//" prefix on path */
|
||||
/* Base doesn't seem to be a directory, ignore it and just strip `//` prefix on path. */
|
||||
BLI_strncpy(path, tmp + 2, FILE_MAX);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* base ignored */
|
||||
/* Base ignored. */
|
||||
BLI_strncpy(path, tmp, FILE_MAX);
|
||||
}
|
||||
|
||||
|
@ -948,8 +1006,8 @@ bool BLI_path_abs(char *path, const char *basepath)
|
|||
BLI_str_replace_char(path + 2, '/', '\\');
|
||||
#endif
|
||||
|
||||
/* ensure this is after correcting for path switch */
|
||||
BLI_path_normalize(NULL, path);
|
||||
/* Ensure this is after correcting for path switch. */
|
||||
BLI_path_normalize(path);
|
||||
|
||||
return wasrelative;
|
||||
}
|
||||
|
@ -960,7 +1018,7 @@ bool BLI_path_is_abs_from_cwd(const char *path)
|
|||
const int path_len_clamp = BLI_strnlen(path, 3);
|
||||
|
||||
#ifdef WIN32
|
||||
if ((path_len_clamp >= 3 && BLI_path_is_abs(path)) || BLI_path_is_unc(path)) {
|
||||
if ((path_len_clamp >= 3 && BLI_path_is_abs_win32(path)) || BLI_path_is_unc(path)) {
|
||||
is_abs = true;
|
||||
}
|
||||
#else
|
||||
|
@ -979,7 +1037,7 @@ bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
|
|||
|
||||
if (!BLI_path_is_abs_from_cwd(path)) {
|
||||
char cwd[FILE_MAX];
|
||||
/* in case the full path to the blend isn't used */
|
||||
/* In case the full path to the blend isn't used. */
|
||||
if (BLI_current_working_dir(cwd, sizeof(cwd))) {
|
||||
char origpath[FILE_MAX];
|
||||
BLI_strncpy(origpath, path, FILE_MAX);
|
||||
|
@ -996,7 +1054,7 @@ bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
|
|||
|
||||
#ifdef _WIN32
|
||||
/**
|
||||
* Tries appending each of the semicolon-separated extensions in the PATHEXT
|
||||
* Tries appending each of the semicolon-separated extensions in the `PATHEXT`
|
||||
* environment variable (Windows-only) onto `name` in turn until such a file is found.
|
||||
* Returns success/failure.
|
||||
*/
|
||||
|
@ -1007,7 +1065,7 @@ bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen)
|
|||
|
||||
type = BLI_exists(name);
|
||||
if ((type == 0) || S_ISDIR(type)) {
|
||||
/* typically 3-5, ".EXE", ".BAT"... etc */
|
||||
/* Typically 3-5, ".EXE", ".BAT"... etc. */
|
||||
const int ext_max = 12;
|
||||
const char *ext = BLI_getenv("PATHEXT");
|
||||
if (ext) {
|
||||
|
@ -1016,7 +1074,7 @@ bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen)
|
|||
char *filename_ext;
|
||||
const char *ext_next;
|
||||
|
||||
/* null terminated in the loop */
|
||||
/* Null terminated in the loop. */
|
||||
memcpy(filename, name, name_len);
|
||||
filename_ext = filename + name_len;
|
||||
|
||||
|
@ -1101,11 +1159,9 @@ bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *na
|
|||
|
||||
void BLI_setenv(const char *env, const char *val)
|
||||
{
|
||||
/* free windows */
|
||||
|
||||
#if (defined(_WIN32) || defined(_WIN64))
|
||||
/* MS-Windows. */
|
||||
uputenv(env, val);
|
||||
|
||||
#else
|
||||
/* Linux/macOS/BSD */
|
||||
if (val) {
|
||||
|
@ -1158,7 +1214,7 @@ bool BLI_make_existing_file(const char *name)
|
|||
char di[FILE_MAX];
|
||||
BLI_split_dir_part(name, di, sizeof(di));
|
||||
|
||||
/* make if the dir doesn't exist */
|
||||
/* Make if the dir doesn't exist. */
|
||||
return BLI_dir_create_recursive(di);
|
||||
}
|
||||
|
||||
|
@ -1260,7 +1316,7 @@ bool BLI_path_extension_glob_validate(char *ext_fnmatch)
|
|||
/* Non-wildcard char, we can break here and consider the pattern valid. */
|
||||
return false;
|
||||
}
|
||||
/* So far, only wildcards in last group of the pattern... */
|
||||
/* So far, only wildcards in last group of the pattern. */
|
||||
only_wildcards = true;
|
||||
}
|
||||
/* Only one group in the pattern, so even if its only made of wildcard(s),
|
||||
|
@ -1352,7 +1408,7 @@ void BLI_split_dirfile(
|
|||
|
||||
if (dir) {
|
||||
if (lslash) {
|
||||
/* +1 to include the slash and the last char */
|
||||
/* +1 to include the slash and the last char. */
|
||||
BLI_strncpy(dir, string, MIN2(dirlen, lslash + 1));
|
||||
}
|
||||
else {
|
||||
|
@ -1429,7 +1485,7 @@ size_t BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__
|
|||
}
|
||||
|
||||
if (dirlen >= maxlen) {
|
||||
return dirlen; /* fills the path */
|
||||
return dirlen; /* Fills the path. */
|
||||
}
|
||||
|
||||
return dirlen + BLI_strncpy_rlen(dst + dirlen, file, maxlen - dirlen);
|
||||
|
@ -1470,7 +1526,7 @@ size_t BLI_path_join_array(char *__restrict dst,
|
|||
}
|
||||
|
||||
#ifdef WIN32
|
||||
/* Special case "//" for relative paths, don't use separator #SEP
|
||||
/* Special case `//` for relative paths, don't use separator #SEP
|
||||
* as this has a special meaning on both WIN32 & UNIX.
|
||||
* Without this check joining `"//", "path"`. results in `"//\path"`. */
|
||||
if (ofs != 0) {
|
||||
|
@ -1519,7 +1575,7 @@ size_t BLI_path_join_array(char *__restrict dst,
|
|||
}
|
||||
|
||||
if (len != 0) {
|
||||
/* the very first path may have a slash at the end */
|
||||
/* The very first path may have a slash at the end. */
|
||||
if (ofs && !BLI_path_slash_is_native_compat(dst[ofs - 1])) {
|
||||
dst[ofs++] = SEP;
|
||||
if (ofs == dst_last) {
|
||||
|
@ -1574,7 +1630,7 @@ static bool path_name_at_index_forward(const char *__restrict path,
|
|||
if ((c == '\0') || BLI_path_slash_is_native_compat(c)) {
|
||||
if (prev + 1 != i) {
|
||||
prev += 1;
|
||||
/* Skip '/./' (behave as if they don't exist). */
|
||||
/* Skip `/./` (behave as if they don't exist). */
|
||||
if (!((i - prev == 1) && (prev != 0) && (path[prev] == '.'))) {
|
||||
if (index_step == index) {
|
||||
*r_offset = prev;
|
||||
|
@ -1609,7 +1665,7 @@ static bool path_name_at_index_backward(const char *__restrict path,
|
|||
if ((c == '\0') || BLI_path_slash_is_native_compat(c)) {
|
||||
if (prev - 1 != i) {
|
||||
i += 1;
|
||||
/* Skip '/./' (behave as if they don't exist). */
|
||||
/* Skip `/./` (behave as if they don't exist). */
|
||||
if (!((prev - i == 1) && (i != 0) && (path[i] == '.'))) {
|
||||
if (index_step == index) {
|
||||
*r_offset = i;
|
||||
|
@ -1644,15 +1700,15 @@ bool BLI_path_contains(const char *container_path, const char *containee_path)
|
|||
char containee_native[PATH_MAX];
|
||||
|
||||
/* Keep space for a trailing slash. If the path is truncated by this, the containee path is
|
||||
* longer than PATH_MAX and the result is ill-defined. */
|
||||
* longer than #PATH_MAX and the result is ill-defined. */
|
||||
BLI_strncpy(container_native, container_path, PATH_MAX - 1);
|
||||
BLI_strncpy(containee_native, containee_path, PATH_MAX);
|
||||
|
||||
BLI_path_slash_native(container_native);
|
||||
BLI_path_slash_native(containee_native);
|
||||
|
||||
BLI_path_normalize(NULL, container_native);
|
||||
BLI_path_normalize(NULL, containee_native);
|
||||
BLI_path_normalize(container_native);
|
||||
BLI_path_normalize(containee_native);
|
||||
|
||||
#ifdef WIN32
|
||||
BLI_str_tolower_ascii(container_native, PATH_MAX);
|
||||
|
@ -1755,8 +1811,8 @@ int BLI_path_cmp_normalized(const char *p1, const char *p2)
|
|||
BLI_path_slash_native(norm_p1);
|
||||
BLI_path_slash_native(norm_p2);
|
||||
|
||||
BLI_path_normalize(NULL, norm_p1);
|
||||
BLI_path_normalize(NULL, norm_p2);
|
||||
BLI_path_normalize(norm_p1);
|
||||
BLI_path_normalize(norm_p2);
|
||||
|
||||
return BLI_path_cmp(norm_p1, norm_p2);
|
||||
}
|
||||
|
|
|
@ -413,7 +413,9 @@ char32_t BLI_str_utf32_char_to_upper(const char32_t wc)
|
|||
{
|
||||
if (wc < U'\xFF') { /* Latin. */
|
||||
if ((wc <= U'z' && wc >= U'a') || (wc <= U'\xF6' && wc >= U'\xE0') ||
|
||||
(wc <= U'\xFE' && wc >= U'\xF8')) {
|
||||
/* Correct but the first case is know, only check the second */
|
||||
// (wc <= U'\xFE' && wc >= U'\xF8')
|
||||
(wc >= U'\xF8')) {
|
||||
return wc - 32;
|
||||
}
|
||||
return wc;
|
||||
|
|
|
@ -42,29 +42,41 @@ static char *str_replace_char_strdup(const char *str, char src, char dst)
|
|||
/** \name Tests for: #BLI_path_normalize
|
||||
* \{ */
|
||||
|
||||
#define NORMALIZE_WITH_BASEDIR(input, input_base, output_expect) \
|
||||
#define NORMALIZE(input, output_expect) \
|
||||
{ \
|
||||
char path[FILE_MAX] = input; \
|
||||
const char *input_base_test = input_base; \
|
||||
if (SEP == '\\') { \
|
||||
str_replace_char_with_relative_exception(path, '/', '\\'); \
|
||||
input_base_test = str_replace_char_strdup(input_base_test, '/', '\\'); \
|
||||
} \
|
||||
BLI_path_normalize(input_base_test, path); \
|
||||
BLI_path_normalize(path); \
|
||||
if (SEP == '\\') { \
|
||||
BLI_str_replace_char(path, '\\', '/'); \
|
||||
if (input_base_test) { \
|
||||
free((void *)input_base_test); \
|
||||
} \
|
||||
} \
|
||||
EXPECT_STREQ(path, output_expect); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define NORMALIZE(input, output_expect) NORMALIZE_WITH_BASEDIR(input, nullptr, output_expect)
|
||||
/* #BLI_path_normalize: do nothing. */
|
||||
TEST(path_util, Normalize_Nop)
|
||||
{
|
||||
NORMALIZE(".", ".");
|
||||
NORMALIZE("./", "./");
|
||||
NORMALIZE("/", "/");
|
||||
NORMALIZE("//", "//");
|
||||
NORMALIZE("//a", "//a");
|
||||
}
|
||||
|
||||
TEST(path_util, Normalize_NopRelative)
|
||||
{
|
||||
NORMALIZE("..", "..");
|
||||
NORMALIZE("../", "../");
|
||||
NORMALIZE("../", "../");
|
||||
NORMALIZE("../..", "../..");
|
||||
NORMALIZE("../../", "../../");
|
||||
}
|
||||
|
||||
/* #BLI_path_normalize: "/./" -> "/" */
|
||||
TEST(path_util, Clean_Dot)
|
||||
TEST(path_util, Normalize_Dot)
|
||||
{
|
||||
NORMALIZE("/./", "/");
|
||||
NORMALIZE("/a/./b/./c/./", "/a/b/c/");
|
||||
|
@ -72,28 +84,68 @@ TEST(path_util, Clean_Dot)
|
|||
NORMALIZE("/a/./././b/", "/a/b/");
|
||||
}
|
||||
/* #BLI_path_normalize: complex "/./" -> "/", "//" -> "/", "./path/../" -> "./". */
|
||||
TEST(path_util, Clean_Complex)
|
||||
TEST(path_util, Normalize_Complex)
|
||||
{
|
||||
NORMALIZE("/a/./b/./c/./.././.././", "/a/");
|
||||
NORMALIZE("/a//.//b//.//c//.//..//.//..//.//", "/a/");
|
||||
}
|
||||
/* #BLI_path_normalize: "//" -> "/" */
|
||||
TEST(path_util, Clean_DoubleSlash)
|
||||
TEST(path_util, Normalize_DoubleSlash)
|
||||
{
|
||||
NORMALIZE("//", "//"); /* Exception, double forward slash. */
|
||||
NORMALIZE(".//", "./");
|
||||
NORMALIZE("a////", "a/");
|
||||
NORMALIZE("./a////", "./a/");
|
||||
NORMALIZE("./a////", "a/");
|
||||
}
|
||||
/* #BLI_path_normalize: "foo/bar/../" -> "foo/" */
|
||||
TEST(path_util, Clean_Parent)
|
||||
TEST(path_util, Normalize_Parent)
|
||||
{
|
||||
NORMALIZE("/a/b/c/../../../", "/");
|
||||
NORMALIZE("/a/../a/b/../b/c/../c/", "/a/b/c/");
|
||||
NORMALIZE_WITH_BASEDIR("//../", "/a/b/c/", "/a/b/");
|
||||
}
|
||||
/* #BLI_path_normalize: with too many "/../", match Python's behavior. */
|
||||
TEST(path_util, Normalize_UnbalancedAbsolute)
|
||||
{
|
||||
NORMALIZE("/../", "/");
|
||||
NORMALIZE("/../a", "/a");
|
||||
NORMALIZE("/a/b/c/../../../../../d", "/d");
|
||||
NORMALIZE("/a/b/c/../../../../d", "/d");
|
||||
NORMALIZE("/a/b/c/../../../d", "/d");
|
||||
}
|
||||
|
||||
/* #BLI_path_normalize: with relative paths that result in leading "../". */
|
||||
TEST(path_util, Normalize_UnbalancedRelative)
|
||||
{
|
||||
NORMALIZE("./a/b/c/../../../", ".");
|
||||
NORMALIZE("a/b/c/../../../", ".");
|
||||
NORMALIZE("//a/b/c/../../../", "//");
|
||||
|
||||
NORMALIZE("./a/../../../", "../../");
|
||||
NORMALIZE("a/../../../", "../../");
|
||||
|
||||
NORMALIZE("///a/../../../", "//../../");
|
||||
NORMALIZE("//./a/../../../", "//../../");
|
||||
|
||||
NORMALIZE("../a/../../../", "../../../");
|
||||
NORMALIZE("a/b/../c/../../d/../../../e/../../../../f", "../../../../../f");
|
||||
NORMALIZE(".../.../a/.../b/../c/../../d/../../../e/../../../.../../f", "../f");
|
||||
}
|
||||
|
||||
TEST(path_util, Normalize_UnbalancedRelativeTrailing)
|
||||
{
|
||||
NORMALIZE("./a/b/c/../../..", ".");
|
||||
NORMALIZE("a/b/c/../../..", ".");
|
||||
NORMALIZE("//a/b/c/../../..", "//");
|
||||
|
||||
NORMALIZE("./a/../../..", "../..");
|
||||
NORMALIZE("a/../../..", "../..");
|
||||
|
||||
NORMALIZE("///a/../../..", "//../..");
|
||||
NORMALIZE("//./a/../../..", "//../..");
|
||||
|
||||
NORMALIZE("../a/../../..", "../../..");
|
||||
}
|
||||
|
||||
#undef NORMALIZE_WITH_BASEDIR
|
||||
#undef NORMALIZE
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -516,7 +516,8 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab
|
|||
char name1[FILE_MAX];
|
||||
|
||||
BLI_strncpy(name1, filepath, sizeof(name1));
|
||||
BLI_path_normalize(relabase, name1);
|
||||
BLI_path_abs(name1, relabase);
|
||||
BLI_path_normalize(name1);
|
||||
|
||||
// printf("blo_find_main: relabase %s\n", relabase);
|
||||
// printf("blo_find_main: original in %s\n", filepath);
|
||||
|
@ -2695,7 +2696,8 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
|
|||
|
||||
/* Make sure we have full path in lib->filepath_abs */
|
||||
BLI_strncpy(lib->filepath_abs, lib->filepath, sizeof(lib->filepath));
|
||||
BLI_path_normalize(fd->relabase, lib->filepath_abs);
|
||||
BLI_path_abs(lib->filepath_abs, fd->relabase);
|
||||
BLI_path_normalize(lib->filepath_abs);
|
||||
|
||||
// printf("direct_link_library: filepath %s\n", lib->filepath);
|
||||
// printf("direct_link_library: filepath_abs %s\n", lib->filepath_abs);
|
||||
|
|
|
@ -1474,13 +1474,13 @@ bool BLO_write_file(Main *mainvar,
|
|||
/* Normalize the paths in case there is some subtle difference (so they can be compared). */
|
||||
if (relbase_valid) {
|
||||
BLI_split_dir_part(mainvar->filepath, dir_src, sizeof(dir_src));
|
||||
BLI_path_normalize(nullptr, dir_src);
|
||||
BLI_path_normalize(dir_src);
|
||||
}
|
||||
else {
|
||||
dir_src[0] = '\0';
|
||||
}
|
||||
BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst));
|
||||
BLI_path_normalize(nullptr, dir_dst);
|
||||
BLI_path_normalize(dir_dst);
|
||||
|
||||
/* Only for relative, not relative-all, as this means making existing paths relative. */
|
||||
if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
|
||||
|
|
|
@ -81,11 +81,8 @@ BLI_INLINE void bmesh_calc_tessellation_for_face_impl(BMLoop *(*looptris)[3],
|
|||
efa->no, l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co);
|
||||
}
|
||||
|
||||
if (UNLIKELY(is_quad_flip_v3_first_third_fast_with_normal(l_ptr_a[0]->v->co,
|
||||
l_ptr_a[1]->v->co,
|
||||
l_ptr_a[2]->v->co,
|
||||
l_ptr_b[2]->v->co,
|
||||
efa->no))) {
|
||||
if (UNLIKELY(is_quad_flip_v3_first_third_fast(
|
||||
l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) {
|
||||
/* Flip out of degenerate 0-2 state. */
|
||||
l_ptr_a[2] = l_ptr_b[2];
|
||||
l_ptr_b[0] = l_ptr_a[1];
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "GPU_context.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
|
||||
#include "eevee_private.h"
|
||||
|
@ -348,6 +350,16 @@ static void eevee_draw_scene(void *vedata)
|
|||
EEVEE_temporal_sampling_reset(vedata);
|
||||
stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
|
||||
}
|
||||
|
||||
/* Perform render step between samples to allow flushing of freed temporary GPUBackend
|
||||
* resources. This prevents the GPU backend accumulating a high amount of in-flight memory when
|
||||
* performing renders using eevee_draw_scene. e.g. During file thumbnail generation. */
|
||||
if (loop_len > 2) {
|
||||
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
|
||||
GPU_flush();
|
||||
GPU_render_step();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_COMBINED) != 0) {
|
||||
|
|
|
@ -166,7 +166,8 @@ void DepthOfField::sync()
|
|||
/* Now that we know the maximum render resolution of every view, using depth of field, allocate
|
||||
* the reduced buffers. Color needs to be signed format here. See note in shader for
|
||||
* explanation. Do not use texture pool because of needs mipmaps. */
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT |
|
||||
GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW;
|
||||
reduced_color_tx_.ensure_2d(GPU_RGBA16F, reduce_size, usage, nullptr, DOF_MIP_COUNT);
|
||||
reduced_coc_tx_.ensure_2d(GPU_R16F, reduce_size, usage, nullptr, DOF_MIP_COUNT);
|
||||
reduced_color_tx_.ensure_mip_views();
|
||||
|
|
|
@ -24,7 +24,8 @@ void HiZBuffer::sync()
|
|||
int2 hiz_extent = math::ceil_to_multiple(render_extent, int2(1u << (HIZ_MIP_COUNT - 1)));
|
||||
int2 dispatch_size = math::divide_ceil(hiz_extent, int2(HIZ_GROUP_SIZE));
|
||||
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
|
||||
GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW;
|
||||
hiz_tx_.ensure_2d(GPU_R32F, hiz_extent, usage, nullptr, HIZ_MIP_COUNT);
|
||||
hiz_tx_.ensure_mip_views();
|
||||
GPU_texture_mipmap_mode(hiz_tx_, true, false);
|
||||
|
|
|
@ -628,7 +628,7 @@ struct LightData {
|
|||
float radius_squared;
|
||||
/** NOTE: It is ok to use float3 here. A float is declared right after it.
|
||||
* float3 is also aligned to 16 bytes. */
|
||||
float3 color;
|
||||
packed_float3 color;
|
||||
/** Light Type. */
|
||||
eLightType type;
|
||||
/** Spot size. Aligned to size of float2. */
|
||||
|
|
|
@ -257,7 +257,8 @@ class ShadowModule {
|
|||
/** Tile to physical page mapping. This is an array texture with one layer per view. */
|
||||
Texture render_map_tx_ = {"ShadowRenderMap",
|
||||
GPU_R32UI,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
|
||||
GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW,
|
||||
int2(SHADOW_TILEMAP_RES),
|
||||
64,
|
||||
nullptr,
|
||||
|
|
|
@ -12,7 +12,7 @@ void main()
|
|||
|
||||
/* Display backfacing surfels with a transparent checkerboard grid. */
|
||||
if (!gl_FrontFacing) {
|
||||
ivec2 grid_uv = ivec2(gl_FragCoord) / 5;
|
||||
ivec2 grid_uv = ivec2(gl_FragCoord.xy) / 5;
|
||||
if ((grid_uv.x + grid_uv.y) % 2 == 0) {
|
||||
discard;
|
||||
return;
|
||||
|
|
|
@ -45,7 +45,8 @@ void main()
|
|||
case LIGHT_RECT:
|
||||
case LIGHT_ELLIPSE:
|
||||
case LIGHT_POINT:
|
||||
sphere = Sphere(light._position, light.influence_radius_max);
|
||||
sphere.center = light._position;
|
||||
sphere.radius = light.influence_radius_max;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ bool debug_tilemaps(vec3 P, LightData light)
|
|||
out_color_add = vec4(debug_tile_state_color(tile), 0.0);
|
||||
out_color_mul = vec4(0.0);
|
||||
|
||||
if (ivec2(gl_FragCoord.xy) == ivec2(0)) {
|
||||
if (all(equal(ivec2(gl_FragCoord.xy), ivec2(0)))) {
|
||||
drw_print(light.object_mat);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
|
||||
|
||||
/* TODO(@fclem): Implement. */
|
||||
#define assert(check)
|
||||
#ifndef GPU_METAL
|
||||
# define assert(check)
|
||||
#endif
|
||||
|
||||
/* Remove page ownership from the tile and append it to the cache. */
|
||||
void shadow_page_free(inout ShadowTileData tile)
|
||||
|
|
|
@ -45,7 +45,7 @@ float pixel_size_at(float linear_depth)
|
|||
if (is_persp) {
|
||||
pixel_size *= max(0.01, linear_depth);
|
||||
}
|
||||
return pixel_size * exp2(fb_lod);
|
||||
return pixel_size * exp2(float(fb_lod));
|
||||
}
|
||||
|
||||
void step_bounding_sphere(vec3 vs_near_plane,
|
||||
|
@ -117,6 +117,7 @@ void main()
|
|||
step_bounding_sphere(vs_near_plane, vs_view_direction, t, t + step_size, P, step_radius);
|
||||
vec3 vP = point_world_to_view(P);
|
||||
|
||||
shadow_tag_usage(vP, P, ws_view_direction, step_radius, t, gl_FragCoord.xy * exp2(fb_lod));
|
||||
shadow_tag_usage(
|
||||
vP, P, ws_view_direction, step_radius, t, gl_FragCoord.xy * exp2(float(fb_lod)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ void inflate_bounds(vec3 ls_center, inout vec3 P, inout vec3 lP)
|
|||
{
|
||||
vec3 vP = point_world_to_view(P);
|
||||
|
||||
float inflate_scale = pixel_world_radius * exp2(fb_lod);
|
||||
float inflate_scale = pixel_world_radius * exp2(float(fb_lod));
|
||||
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
|
||||
if (is_persp) {
|
||||
inflate_scale *= -vP.z;
|
||||
|
|
|
@ -45,6 +45,8 @@ void write_depth(ivec2 texel_co, const int lod, ivec2 tile_co, float depth)
|
|||
/* Quantization bias. Equivalent to nextafter in C without all the safety. 1 is not enough. */
|
||||
u_depth += 2;
|
||||
|
||||
/* TOOD(Metal): For Metal, textures will need to be viewed as buffers to workaround missing image
|
||||
* atomics support. */
|
||||
imageAtomicMin(shadow_atlas_img, out_texel, u_depth);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,11 @@
|
|||
/** \name Shadow pipeline
|
||||
* \{ */
|
||||
|
||||
/* NOTE(Metal): As this is implemented using a fundamental data type, this needs to be specified
|
||||
* explicitly as uint for code generation, as the MSLShaderGenerator needs to be able to
|
||||
* distinguish between classes and fundamental types during code generation. */
|
||||
#define SHADOW_TILE_DATA_PACKED "uint"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_shadow_clipmap_clear)
|
||||
.do_static_compilation(true)
|
||||
.local_group_size(SHADOW_CLIPMAP_GROUP_SIZE)
|
||||
|
@ -34,7 +39,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tilemap_init)
|
|||
.do_static_compilation(true)
|
||||
.local_group_size(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES)
|
||||
.storage_buf(0, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.storage_buf(2, Qualifier::READ_WRITE, "ShadowTileMapClip", "tilemaps_clip_buf[]")
|
||||
.storage_buf(4, Qualifier::READ_WRITE, "uvec2", "pages_cached_buf[]")
|
||||
.additional_info("eevee_shared")
|
||||
|
@ -44,7 +49,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_update)
|
|||
.do_static_compilation(true)
|
||||
.local_group_size(1, 1, 1)
|
||||
.storage_buf(0, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.storage_buf(5, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
|
||||
.storage_buf(6, Qualifier::READ, "uint", "resource_ids_buf[]")
|
||||
.additional_info("eevee_shared", "draw_view", "draw_view_culling")
|
||||
|
@ -55,7 +60,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_opaque)
|
|||
.local_group_size(SHADOW_DEPTH_SCAN_GROUP_SIZE, SHADOW_DEPTH_SCAN_GROUP_SIZE)
|
||||
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
|
||||
.storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(6, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
|
||||
.storage_buf(6, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
|
||||
.additional_info("eevee_shared", "draw_view", "draw_view_culling", "eevee_light_data")
|
||||
.compute_source("eevee_shadow_tag_usage_comp.glsl");
|
||||
|
@ -71,7 +76,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_transparent)
|
|||
.vertex_in(0, Type::VEC3, "pos")
|
||||
.storage_buf(4, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
|
||||
.storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(6, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
|
||||
.storage_buf(6, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
|
||||
.push_constant(Type::FLOAT, "pixel_world_radius")
|
||||
.push_constant(Type::IVEC2, "fb_resolution")
|
||||
|
@ -91,7 +96,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_mask)
|
|||
.do_static_compilation(true)
|
||||
.local_group_size(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES)
|
||||
.storage_buf(0, Qualifier::READ, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.additional_info("eevee_shared")
|
||||
.compute_source("eevee_shadow_page_mask_comp.glsl");
|
||||
|
||||
|
@ -99,7 +104,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_free)
|
|||
.do_static_compilation(true)
|
||||
.local_group_size(SHADOW_TILEMAP_LOD0_LEN)
|
||||
.storage_buf(0, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.storage_buf(2, Qualifier::READ_WRITE, "ShadowPagesInfoData", "pages_infos_buf")
|
||||
.storage_buf(3, Qualifier::READ_WRITE, "uint", "pages_free_buf[]")
|
||||
.storage_buf(4, Qualifier::READ_WRITE, "uvec2", "pages_cached_buf[]")
|
||||
|
@ -110,7 +115,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_defrag)
|
|||
.do_static_compilation(true)
|
||||
.local_group_size(1)
|
||||
.typedef_source("draw_shader_shared.h")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.storage_buf(2, Qualifier::READ_WRITE, "ShadowPagesInfoData", "pages_infos_buf")
|
||||
.storage_buf(3, Qualifier::READ_WRITE, "uint", "pages_free_buf[]")
|
||||
.storage_buf(4, Qualifier::READ_WRITE, "uvec2", "pages_cached_buf[]")
|
||||
|
@ -124,7 +129,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_allocate)
|
|||
.local_group_size(SHADOW_TILEMAP_LOD0_LEN)
|
||||
.typedef_source("draw_shader_shared.h")
|
||||
.storage_buf(0, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.storage_buf(2, Qualifier::READ_WRITE, "ShadowPagesInfoData", "pages_infos_buf")
|
||||
.storage_buf(3, Qualifier::READ_WRITE, "uint", "pages_free_buf[]")
|
||||
.storage_buf(4, Qualifier::READ_WRITE, "uvec2", "pages_cached_buf[]")
|
||||
|
@ -137,7 +142,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tilemap_finalize)
|
|||
.typedef_source("draw_shader_shared.h")
|
||||
.local_group_size(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES)
|
||||
.storage_buf(0, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.storage_buf(2, Qualifier::READ_WRITE, "ShadowPagesInfoData", "pages_infos_buf")
|
||||
.storage_buf(3, Qualifier::WRITE, "ViewMatrices", "view_infos_buf[64]")
|
||||
.storage_buf(4, Qualifier::READ_WRITE, "ShadowStatistics", "statistics_buf")
|
||||
|
@ -183,11 +188,12 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_debug)
|
|||
.do_static_compilation(true)
|
||||
.additional_info("eevee_shared")
|
||||
.storage_buf(5, Qualifier::READ, "ShadowTileMapData", "tilemaps_buf[]")
|
||||
.storage_buf(6, Qualifier::READ, "ShadowTileDataPacked", "tiles_buf[]")
|
||||
.storage_buf(6, Qualifier::READ, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
|
||||
.fragment_out(0, Type::VEC4, "out_color_add", DualBlend::SRC_0)
|
||||
.fragment_out(0, Type::VEC4, "out_color_mul", DualBlend::SRC_1)
|
||||
.push_constant(Type::INT, "debug_mode")
|
||||
.push_constant(Type::INT, "debug_tilemap_index")
|
||||
.depth_write(DepthWrite::ANY)
|
||||
.fragment_source("eevee_shadow_debug_frag.glsl")
|
||||
.additional_info(
|
||||
"draw_fullscreen", "draw_view", "eevee_hiz_data", "eevee_light_data", "eevee_shadow_data");
|
||||
|
|
|
@ -30,46 +30,32 @@
|
|||
/** \name Update Loose Geometry
|
||||
* \{ */
|
||||
|
||||
static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MeshBufferCache *cache)
|
||||
static void extract_set_bits(const blender::BitSpan bits, blender::MutableSpan<int> indices)
|
||||
{
|
||||
using namespace blender;
|
||||
BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__);
|
||||
|
||||
const bke::LooseEdgeCache &loose_edges = mr->me->loose_edges();
|
||||
if (loose_edges.count > 0) {
|
||||
cache->loose_geom.edges.reinitialize(loose_edges.count);
|
||||
|
||||
int count = 0;
|
||||
for (const int64_t i : loose_edges.is_loose_bits.index_range()) {
|
||||
if (loose_edges.is_loose_bits[i]) {
|
||||
cache->loose_geom.edges[count] = int(i);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Tag verts as not loose. */
|
||||
for (const int2 &edge : mr->edges) {
|
||||
BLI_BITMAP_ENABLE(lvert_map, edge[0]);
|
||||
BLI_BITMAP_ENABLE(lvert_map, edge[1]);
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
Array<int> loose_verts(mr->vert_len);
|
||||
for (int v = 0; v < mr->vert_len; v++) {
|
||||
if (!BLI_BITMAP_TEST(lvert_map, v)) {
|
||||
loose_verts[count] = v;
|
||||
for (const int64_t i : bits.index_range()) {
|
||||
if (bits[i]) {
|
||||
indices[count] = int(i);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count < mr->vert_len) {
|
||||
cache->loose_geom.verts = loose_verts.as_span().take_front(count);
|
||||
}
|
||||
else {
|
||||
cache->loose_geom.verts = std::move(loose_verts);
|
||||
BLI_assert(count == indices.size());
|
||||
}
|
||||
|
||||
static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MeshBufferCache *cache)
|
||||
{
|
||||
using namespace blender;
|
||||
const bke::LooseEdgeCache &loose_edges = mr->me->loose_edges();
|
||||
if (loose_edges.count > 0) {
|
||||
cache->loose_geom.edges.reinitialize(loose_edges.count);
|
||||
extract_set_bits(loose_edges.is_loose_bits, cache->loose_geom.edges);
|
||||
}
|
||||
|
||||
MEM_freeN(lvert_map);
|
||||
const bke::LooseVertCache &loose_verts = mr->me->loose_verts();
|
||||
if (loose_verts.count > 0) {
|
||||
cache->loose_geom.verts.reinitialize(loose_verts.count);
|
||||
extract_set_bits(loose_verts.is_loose_bits, cache->loose_geom.verts);
|
||||
}
|
||||
}
|
||||
|
||||
static void mesh_render_data_loose_verts_bm(const MeshRenderData *mr,
|
||||
|
|
|
@ -33,7 +33,12 @@
|
|||
|
||||
BLI_INLINE eParticleRefineShaderType drw_curves_shader_type_get()
|
||||
{
|
||||
if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) {
|
||||
/* NOTE: Curve refine is faster using transform feedback via vertex processing pipeline with
|
||||
* Metal and Apple Silicon GPUs. This is also because vertex work can more easily be executed in
|
||||
* parallel with fragment work, whereas compute inserts an explicit dependency,
|
||||
* due to switching of command encoder types. */
|
||||
if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support() &&
|
||||
(GPU_backend_get_type() != GPU_BACKEND_METAL)) {
|
||||
return PART_REFINE_SHADER_COMPUTE;
|
||||
}
|
||||
if (GPU_transform_feedback_support()) {
|
||||
|
|
|
@ -36,7 +36,12 @@
|
|||
|
||||
BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get()
|
||||
{
|
||||
if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) {
|
||||
/* NOTE: Hair refine is faster using transform feedback via vertex processing pipeline with Metal
|
||||
* and Apple Silicon GPUs. This is also because vertex work can more easily be executed in
|
||||
* parallel with fragment work, whereas compute inserts an explicit dependency,
|
||||
* due to switching of command encoder types. */
|
||||
if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support() &&
|
||||
(GPU_backend_get_type() != GPU_BACKEND_METAL)) {
|
||||
return PART_REFINE_SHADER_COMPUTE;
|
||||
}
|
||||
if (GPU_transform_feedback_support()) {
|
||||
|
|
|
@ -350,6 +350,16 @@ struct DRWDebugVert {
|
|||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(DRWDebugVert, 16)
|
||||
|
||||
inline DRWDebugVert debug_vert_make(uint in_pos0, uint in_pos1, uint in_pos2, uint in_vert_color)
|
||||
{
|
||||
DRWDebugVert debug_vert;
|
||||
debug_vert.pos0 = in_pos0;
|
||||
debug_vert.pos1 = in_pos1;
|
||||
debug_vert.pos2 = in_pos2;
|
||||
debug_vert.vert_color = in_vert_color;
|
||||
return debug_vert;
|
||||
}
|
||||
|
||||
/* Take the header (DrawCommand) into account. */
|
||||
#define DRW_DEBUG_DRAW_VERT_MAX (64 * 8192) - 1
|
||||
|
||||
|
|
|
@ -34,9 +34,9 @@ uint drw_debug_color_pack(vec4 v_color)
|
|||
|
||||
void drw_debug_line(inout uint vertid, vec3 v1, vec3 v2, uint v_color)
|
||||
{
|
||||
drw_debug_verts_buf[vertid++] = DRWDebugVert(
|
||||
drw_debug_verts_buf[vertid++] = debug_vert_make(
|
||||
floatBitsToUint(v1.x), floatBitsToUint(v1.y), floatBitsToUint(v1.z), v_color);
|
||||
drw_debug_verts_buf[vertid++] = DRWDebugVert(
|
||||
drw_debug_verts_buf[vertid++] = debug_vert_make(
|
||||
floatBitsToUint(v2.x), floatBitsToUint(v2.y), floatBitsToUint(v2.z), v_color);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,9 @@ void FONT_OT_text_cut(struct wmOperatorType *ot);
|
|||
void FONT_OT_text_paste(struct wmOperatorType *ot);
|
||||
void FONT_OT_text_paste_from_file(struct wmOperatorType *ot);
|
||||
|
||||
void FONT_OT_selection_set(struct wmOperatorType *ot);
|
||||
void FONT_OT_select_word(struct wmOperatorType *ot);
|
||||
|
||||
void FONT_OT_move(struct wmOperatorType *ot);
|
||||
void FONT_OT_move_select(struct wmOperatorType *ot);
|
||||
void FONT_OT_delete(struct wmOperatorType *ot);
|
||||
|
|
|
@ -40,6 +40,9 @@ void ED_operatortypes_curve(void)
|
|||
WM_operatortype_append(FONT_OT_text_paste);
|
||||
WM_operatortype_append(FONT_OT_text_paste_from_file);
|
||||
|
||||
WM_operatortype_append(FONT_OT_selection_set);
|
||||
WM_operatortype_append(FONT_OT_select_word);
|
||||
|
||||
WM_operatortype_append(FONT_OT_move);
|
||||
WM_operatortype_append(FONT_OT_move_select);
|
||||
WM_operatortype_append(FONT_OT_delete);
|
||||
|
|
|
@ -1805,6 +1805,142 @@ void FONT_OT_text_insert(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Font Selection Operator
|
||||
* \{ */
|
||||
|
||||
static int font_cursor_text_index_from_event(bContext *C, Object *obedit, const wmEvent *event)
|
||||
{
|
||||
Curve *cu = obedit->data;
|
||||
|
||||
/* Calculate a plane from the text object's orientation. */
|
||||
float plane[4];
|
||||
plane_from_point_normal_v3(plane, obedit->object_to_world[3], obedit->object_to_world[2]);
|
||||
|
||||
/* Convert Mouse location in region to 3D location in world space. */
|
||||
float mal_fl[2] = {(float)event->mval[0], (float)event->mval[1]};
|
||||
float mouse_loc[3];
|
||||
ED_view3d_win_to_3d_on_plane(CTX_wm_region(C), plane, mal_fl, true, mouse_loc);
|
||||
|
||||
/* Convert to object space and scale by font size. */
|
||||
mul_m4_v3(obedit->world_to_object, mouse_loc);
|
||||
|
||||
float curs_loc[2] = {mouse_loc[0] / cu->fsize, mouse_loc[1] / cu->fsize};
|
||||
return BKE_vfont_cursor_to_text_index(obedit, curs_loc);
|
||||
}
|
||||
|
||||
static void font_cursor_set_apply(bContext *C, const wmEvent *event)
|
||||
{
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
Object *ob = DEG_get_evaluated_object(depsgraph, CTX_data_active_object(C));
|
||||
Curve *cu = ob->data;
|
||||
EditFont *ef = cu->editfont;
|
||||
BLI_assert(ef->len >= 0);
|
||||
|
||||
const int string_offset = font_cursor_text_index_from_event(C, ob, event);
|
||||
|
||||
if (string_offset > ef->len || string_offset < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cu->curinfo = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0];
|
||||
|
||||
if (ob->totcol > 0) {
|
||||
ob->actcol = cu->curinfo.mat_nr;
|
||||
if (ob->actcol < 1) {
|
||||
ob->actcol = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ef->selboxes && (ef->selstart == 0)) {
|
||||
if (ef->pos == 0) {
|
||||
ef->selstart = ef->selend = 1;
|
||||
}
|
||||
else {
|
||||
ef->selstart = ef->selend = string_offset + 1;
|
||||
}
|
||||
}
|
||||
ef->selend = string_offset;
|
||||
ef->pos = string_offset;
|
||||
|
||||
DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
||||
}
|
||||
|
||||
static int font_selection_set_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
Object *obedit = CTX_data_active_object(C);
|
||||
Curve *cu = obedit->data;
|
||||
EditFont *ef = cu->editfont;
|
||||
|
||||
font_cursor_set_apply(C, event);
|
||||
ef->selstart = 0;
|
||||
ef->selend = 0;
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int font_selection_set_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
|
||||
{
|
||||
switch (event->type) {
|
||||
case LEFTMOUSE:
|
||||
if (event->val == KM_RELEASE) {
|
||||
font_cursor_set_apply(C, event);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
break;
|
||||
case MIDDLEMOUSE:
|
||||
case RIGHTMOUSE:
|
||||
return OPERATOR_FINISHED;
|
||||
case MOUSEMOVE:
|
||||
font_cursor_set_apply(C, event);
|
||||
break;
|
||||
}
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
void FONT_OT_selection_set(struct wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Set Selection";
|
||||
ot->idname = "FONT_OT_selection_set";
|
||||
ot->description = "Set cursor selection";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = font_selection_set_invoke;
|
||||
ot->modal = font_selection_set_modal;
|
||||
ot->poll = ED_operator_editfont;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Select Word Operator
|
||||
* \{ */
|
||||
|
||||
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);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void FONT_OT_select_word(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Select Word";
|
||||
ot->idname = "FONT_OT_select_word";
|
||||
ot->description = "Select word under cursor";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = font_select_word_exec;
|
||||
ot->poll = ED_operator_editfont;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Text-Box Add Operator
|
||||
* \{ */
|
||||
|
|
|
@ -151,6 +151,10 @@ short ED_transform_calc_orientation_from_type_ex(const struct Scene *scene,
|
|||
int pivot_point,
|
||||
float r_mat[3][3]);
|
||||
|
||||
bool ED_transform_calc_pivot_pos(const struct bContext *C,
|
||||
const short pivot_type,
|
||||
float r_pivot_pos[3]);
|
||||
|
||||
/* transform gizmos */
|
||||
|
||||
void VIEW3D_GGT_xform_gizmo(struct wmGizmoGroupType *gzgt);
|
||||
|
@ -201,7 +205,8 @@ struct TransformCalcParams {
|
|||
*/
|
||||
int ED_transform_calc_gizmo_stats(const struct bContext *C,
|
||||
const struct TransformCalcParams *params,
|
||||
struct TransformBounds *tbounds);
|
||||
struct TransformBounds *tbounds,
|
||||
struct RegionView3D *rv3d);
|
||||
|
||||
/**
|
||||
* Iterates over all the strips and finds the closest snapping candidate of either \a frame_1 or \a
|
||||
|
|
|
@ -154,7 +154,7 @@ struct SculptUndoNodeGeometry {
|
|||
CustomData ldata;
|
||||
CustomData pdata;
|
||||
int *poly_offset_indices;
|
||||
blender::ImplicitSharingInfo *poly_offsets_sharing_info;
|
||||
const blender::ImplicitSharingInfo *poly_offsets_sharing_info;
|
||||
int totvert;
|
||||
int totedge;
|
||||
int totloop;
|
||||
|
|
|
@ -118,7 +118,6 @@ static void tracking_segment_knot_cb(void *userdata,
|
|||
float val)
|
||||
{
|
||||
TrackMotionCurveUserData *data = (TrackMotionCurveUserData *)userdata;
|
||||
int sel = 0, sel_flag;
|
||||
|
||||
if (track != data->act_track) {
|
||||
return;
|
||||
|
@ -127,8 +126,9 @@ static void tracking_segment_knot_cb(void *userdata,
|
|||
return;
|
||||
}
|
||||
|
||||
sel_flag = value_source == CLIP_VALUE_SOURCE_SPEED_X ? MARKER_GRAPH_SEL_X : MARKER_GRAPH_SEL_Y;
|
||||
sel = (marker->flag & sel_flag) ? 1 : 0;
|
||||
const int sel_flag = value_source == CLIP_VALUE_SOURCE_SPEED_X ? MARKER_GRAPH_SEL_X :
|
||||
MARKER_GRAPH_SEL_Y;
|
||||
const bool sel = (marker->flag & sel_flag) != 0;
|
||||
|
||||
if (sel == data->sel) {
|
||||
immUniformThemeColor(sel ? TH_HANDLE_VERTEX_SELECT : TH_HANDLE_VERTEX);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue