diff options
author | Dalai Felinto <dalai@blender.org> | 2020-12-17 19:39:49 +0300 |
---|---|---|
committer | Dalai Felinto <dalai@blender.org> | 2020-12-17 19:39:49 +0300 |
commit | 42b0389a9b05e51e17ba79540e88cd446b52fae7 (patch) | |
tree | ee17716640f1b1133ae2ab4ca65cd92a63d09f15 | |
parent | c5a17d5ea1ff786cb91cbcf3f12cd02f730c4143 (diff) | |
parent | e7b698327cd91b371ff4fd43d1c117637224fded (diff) |
Merge remote-tracking branch 'origin/master' into geometry-nodesgeometry-nodes
656 files changed, 19910 insertions, 5969 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d7cf4e325c..c8ae562f6ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,16 +347,21 @@ if(UNIX AND NOT APPLE) endif() option(WITH_PYTHON_INSTALL "Copy system python into the blender install folder" ON) + +if((WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE) OR WITH_MOD_FLUID) + option(WITH_PYTHON_NUMPY "Include NumPy in Blender (used by Audaspace and Mantaflow)" ON) +endif() + if(WIN32 OR APPLE) # Windows and macOS have this bundled with Python libraries. -elseif(WITH_PYTHON_INSTALL OR (WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE)) +elseif(WITH_PYTHON_INSTALL OR WITH_PYTHON_NUMPY) set(PYTHON_NUMPY_PATH "" CACHE PATH "Path to python site-packages or dist-packages containing 'numpy' module") mark_as_advanced(PYTHON_NUMPY_PATH) - set(PYTHON_NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_PATH}/numpy/core/include CACHE PATH "Path to the include directory of the numpy module") + set(PYTHON_NUMPY_INCLUDE_DIRS "" CACHE PATH "Path to the include directory of the NumPy module") mark_as_advanced(PYTHON_NUMPY_INCLUDE_DIRS) endif() if(WITH_PYTHON_INSTALL) - option(WITH_PYTHON_INSTALL_NUMPY "Copy system numpy into the blender install folder" ON) + option(WITH_PYTHON_INSTALL_NUMPY "Copy system NumPy into the blender install folder" ON) if(UNIX AND NOT APPLE) option(WITH_PYTHON_INSTALL_REQUESTS "Copy system requests into the blender install folder" ON) @@ -1621,19 +1626,16 @@ if(WITH_PYTHON) if(WIN32 OR APPLE) # Windows and macOS have this bundled with Python libraries. - elseif((WITH_PYTHON_INSTALL AND WITH_PYTHON_INSTALL_NUMPY) OR (WITH_AUDASPACE AND NOT WITH_SYSTEM_AUDASPACE)) + elseif((WITH_PYTHON_INSTALL AND WITH_PYTHON_INSTALL_NUMPY) OR WITH_PYTHON_NUMPY) if(("${PYTHON_NUMPY_PATH}" STREQUAL "") OR (${PYTHON_NUMPY_PATH} MATCHES NOTFOUND)) - find_python_package(numpy) - unset(PYTHON_NUMPY_INCLUDE_DIRS CACHE) - set(PYTHON_NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_PATH}/numpy/core/include CACHE PATH "Path to the include directory of the numpy module") - mark_as_advanced(PYTHON_NUMPY_INCLUDE_DIRS) + find_python_package(numpy "core/include") endif() endif() if(WIN32 OR APPLE) # pass, we have this in lib/python/site-packages elseif(WITH_PYTHON_INSTALL_REQUESTS) - find_python_package(requests) + find_python_package(requests "") endif() endif() diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 202b44f611c..9febeaa6889 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -1139,6 +1139,7 @@ endfunction() function(find_python_package package + relative_include_dir ) string(TOUPPER ${package} _upper_package) @@ -1170,7 +1171,10 @@ function(find_python_package dist-packages vendor-packages NO_DEFAULT_PATH + DOC + "Path to python site-packages or dist-packages containing '${package}' module" ) + mark_as_advanced(PYTHON_${_upper_package}_PATH) if(NOT EXISTS "${PYTHON_${_upper_package}_PATH}") message(WARNING @@ -1188,6 +1192,50 @@ function(find_python_package set(WITH_PYTHON_INSTALL_${_upper_package} OFF PARENT_SCOPE) else() message(STATUS "${package} found at '${PYTHON_${_upper_package}_PATH}'") + + if(NOT "${relative_include_dir}" STREQUAL "") + set(_relative_include_dir "${package}/${relative_include_dir}") + unset(PYTHON_${_upper_package}_INCLUDE_DIRS CACHE) + find_path(PYTHON_${_upper_package}_INCLUDE_DIRS + NAMES + "${_relative_include_dir}" + HINTS + "${PYTHON_LIBPATH}/" + "${PYTHON_LIBPATH}/python${PYTHON_VERSION}/" + "${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/" + PATH_SUFFIXES + "site-packages/" + "dist-packages/" + "vendor-packages/" + NO_DEFAULT_PATH + DOC + "Path to python site-packages or dist-packages containing '${package}' module header files" + ) + mark_as_advanced(PYTHON_${_upper_package}_INCLUDE_DIRS) + + if(NOT EXISTS "${PYTHON_${_upper_package}_INCLUDE_DIRS}") + message(WARNING + "Python package '${package}' include dir path could not be found in:\n" + "'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/site-packages/${_relative_include_dir}', " + "'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/site-packages/${_relative_include_dir}', " + "'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/dist-packages/${_relative_include_dir}', " + "'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/dist-packages/${_relative_include_dir}', " + "'${PYTHON_LIBPATH}/python${PYTHON_VERSION}/vendor-packages/${_relative_include_dir}', " + "'${PYTHON_LIBPATH}/python${_PY_VER_MAJOR}/vendor-packages/${_relative_include_dir}', " + "\n" + "The 'WITH_PYTHON_${_upper_package}' option will be disabled.\n" + "The build will be usable, only add-ons that depend on this package won't be functional." + ) + set(WITH_PYTHON_${_upper_package} OFF PARENT_SCOPE) + else() + set(_temp "${PYTHON_${_upper_package}_INCLUDE_DIRS}/${package}/${relative_include_dir}") + unset(PYTHON_${_upper_package}_INCLUDE_DIRS CACHE) + set(PYTHON_${_upper_package}_INCLUDE_DIRS "${_temp}" + CACHE PATH "Path to the include directory of the ${package} module") + + message(STATUS "${package} include files found at '${PYTHON_${_upper_package}_INCLUDE_DIRS}'") + endif() + endif() endif() endif() endfunction() diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index dd8b286c689..016ae58fde4 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -739,7 +739,7 @@ if(WINDOWS_PYTHON_DEBUG) string(REPLACE "/" "\\" _group_path "${_source_path}") source_group("${_group_path}" FILES "${_source}") endforeach() - + # If the user scripts env var is set, include scripts from there otherwise # include user scripts in the profile folder. if(DEFINED ENV{BLENDER_USER_SCRIPTS}) @@ -750,7 +750,7 @@ if(WINDOWS_PYTHON_DEBUG) # Include the user scripts from the profile folder in the blender_python_user_scripts project. set(USER_SCRIPTS_ROOT "$ENV{appdata}/blender foundation/blender/${BLENDER_VERSION}/scripts") endif() - + file(TO_CMAKE_PATH ${USER_SCRIPTS_ROOT} USER_SCRIPTS_ROOT) FILE(GLOB_RECURSE inFiles "${USER_SCRIPTS_ROOT}/*.*" ) ADD_CUSTOM_TARGET(blender_python_user_scripts SOURCES ${inFiles}) diff --git a/doc/doxygen/doxygen.source.h b/doc/doxygen/doxygen.source.h index 613e513bcc6..510f3fe8ffe 100644 --- a/doc/doxygen/doxygen.source.h +++ b/doc/doxygen/doxygen.source.h @@ -121,6 +121,10 @@ * \ingroup editors */ +/** \defgroup edasset asset + * \ingroup editors + */ + /** \defgroup edcurve curve * \ingroup editors */ diff --git a/extern/README b/extern/README new file mode 100644 index 00000000000..f904fc3e41e --- /dev/null +++ b/extern/README @@ -0,0 +1,4 @@ +When updating a library remember to: + +* Update the README.blender with the corresponding version. +* Update the THIRD-PARTY-LICENSE.txt document diff --git a/extern/audaspace/blender_config.cmake b/extern/audaspace/blender_config.cmake index 1f8f85b9868..12810e2b044 100644 --- a/extern/audaspace/blender_config.cmake +++ b/extern/audaspace/blender_config.cmake @@ -24,6 +24,6 @@ set(JACK_FOUND ${WITH_JACK}) set(LIBSNDFILE_FOUND ${WITH_CODEC_SNDFILE}) set(OPENAL_FOUND ${WITH_OPENAL}) set(PYTHONLIBS_FOUND TRUE) -set(NUMPY_FOUND TRUE) +set(NUMPY_FOUND ${WITH_PYTHON_NUMPY}) set(NUMPY_INCLUDE_DIRS ${PYTHON_NUMPY_INCLUDE_DIRS}) set(SDL_FOUND ${WITH_SDL}) diff --git a/extern/draco/draco/CMakeLists.txt b/extern/draco/draco/CMakeLists.txt index 6f9ffce6b48..d775b39d3ac 100644 --- a/extern/draco/draco/CMakeLists.txt +++ b/extern/draco/draco/CMakeLists.txt @@ -268,4 +268,3 @@ set(INC ) blender_add_lib(draco "${SRC}" "${INC}" "" "${LIB}") - diff --git a/extern/mantaflow/CMakeLists.txt b/extern/mantaflow/CMakeLists.txt index ee155ee8636..ccf272650e3 100644 --- a/extern/mantaflow/CMakeLists.txt +++ b/extern/mantaflow/CMakeLists.txt @@ -85,7 +85,7 @@ if(WIN32) add_definitions(-D_USE_MATH_DEFINES) endif() -if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY) +if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY) add_definitions(-DNUMPY=1) endif() @@ -109,7 +109,7 @@ set(INC_SYS ${ZLIB_INCLUDE_DIRS} ) -if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY) +if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY) list(APPEND INC_SYS ${PYTHON_NUMPY_INCLUDE_DIRS} ) @@ -255,8 +255,7 @@ if(WITH_MANTA_DEPENDENCIES) ${MANTA_DEP}/cnpy/cnpy.h ) endif() - -if(WITH_MANTA_NUMPY AND WITH_PYTHON_INSTALL_NUMPY) +if(WITH_MANTA_NUMPY AND WITH_PYTHON_NUMPY) list(APPEND SRC ${MANTA_PP}/plugin/numpyconvert.cpp ${MANTA_PP}/plugin/tfplugins.cpp diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 1cb29fc6cb0..2f204b2c658 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1570,7 +1570,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): elif entry.type == 'CPU': cpu_devices.append(entry) # Extend all GPU devices with CPU. - if compute_device_type in {'CUDA', 'OPENCL'}: + if compute_device_type in {'CUDA', 'OPTIX', 'OPENCL'}: devices.extend(cpu_devices) return devices diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/blender_device.cpp index ffcaef0b2a9..977f8297de1 100644 --- a/intern/cycles/blender/blender_device.cpp +++ b/intern/cycles/blender/blender_device.cpp @@ -90,8 +90,7 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen mask |= DEVICE_MASK_CUDA; } else if (compute_device == COMPUTE_DEVICE_OPTIX) { - /* Cannot use CPU and OptiX device at the same time right now, so replace mask. */ - mask = DEVICE_MASK_OPTIX; + mask |= DEVICE_MASK_OPTIX; } else if (compute_device == COMPUTE_DEVICE_OPENCL) { mask |= DEVICE_MASK_OPENCL; diff --git a/intern/cycles/bvh/CMakeLists.txt b/intern/cycles/bvh/CMakeLists.txt index 703c69b1797..8cc72359757 100644 --- a/intern/cycles/bvh/CMakeLists.txt +++ b/intern/cycles/bvh/CMakeLists.txt @@ -25,6 +25,7 @@ set(SRC bvh_binning.cpp bvh_build.cpp bvh_embree.cpp + bvh_multi.cpp bvh_node.cpp bvh_optix.cpp bvh_sort.cpp @@ -38,6 +39,7 @@ set(SRC_HEADERS bvh_binning.h bvh_build.h bvh_embree.h + bvh_multi.h bvh_node.h bvh_optix.h bvh_params.h diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp index a51ac4cf4a9..256382e63ba 100644 --- a/intern/cycles/bvh/bvh.cpp +++ b/intern/cycles/bvh/bvh.cpp @@ -17,17 +17,11 @@ #include "bvh/bvh.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" - #include "bvh/bvh2.h" -#include "bvh/bvh_build.h" #include "bvh/bvh_embree.h" -#include "bvh/bvh_node.h" +#include "bvh/bvh_multi.h" #include "bvh/bvh_optix.h" -#include "util/util_foreach.h" #include "util/util_logging.h" #include "util/util_progress.h" @@ -38,14 +32,17 @@ CCL_NAMESPACE_BEGIN const char *bvh_layout_name(BVHLayout layout) { switch (layout) { - case BVH_LAYOUT_BVH2: - return "BVH2"; case BVH_LAYOUT_NONE: return "NONE"; + case BVH_LAYOUT_BVH2: + return "BVH2"; case BVH_LAYOUT_EMBREE: return "EMBREE"; case BVH_LAYOUT_OPTIX: return "OPTIX"; + case BVH_LAYOUT_MULTI_OPTIX: + case BVH_LAYOUT_MULTI_OPTIX_EMBREE: + return "MULTI"; case BVH_LAYOUT_ALL: return "ALL"; } @@ -76,17 +73,6 @@ BVHLayout BVHParams::best_bvh_layout(BVHLayout requested_layout, BVHLayoutMask s return (BVHLayout)(1 << widest_allowed_layout_mask); } -/* Pack Utility */ - -BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i) -{ -} - -int BVHStackEntry::encodeIdx() const -{ - return (node->is_leaf()) ? ~idx : idx; -} - /* BVH */ BVH::BVH(const BVHParams ¶ms_, @@ -99,24 +85,27 @@ BVH::BVH(const BVHParams ¶ms_, BVH *BVH::create(const BVHParams ¶ms, const vector<Geometry *> &geometry, const vector<Object *> &objects, - const Device *device) + Device *device) { switch (params.bvh_layout) { case BVH_LAYOUT_BVH2: return new BVH2(params, geometry, objects); case BVH_LAYOUT_EMBREE: #ifdef WITH_EMBREE - return new BVHEmbree(params, geometry, objects, device); + return new BVHEmbree(params, geometry, objects); #else - (void)device; break; #endif case BVH_LAYOUT_OPTIX: #ifdef WITH_OPTIX - return new BVHOptiX(params, geometry, objects); + return new BVHOptiX(params, geometry, objects, device); #else + (void)device; break; #endif + case BVH_LAYOUT_MULTI_OPTIX: + case BVH_LAYOUT_MULTI_OPTIX_EMBREE: + return new BVHMulti(params, geometry, objects); case BVH_LAYOUT_NONE: case BVH_LAYOUT_ALL: break; @@ -125,399 +114,4 @@ BVH *BVH::create(const BVHParams ¶ms, return NULL; } -/* Building */ - -void BVH::build(Progress &progress, Stats *) -{ - progress.set_substatus("Building BVH"); - - /* build nodes */ - BVHBuild bvh_build(objects, - pack.prim_type, - pack.prim_index, - pack.prim_object, - pack.prim_time, - params, - progress); - BVHNode *bvh2_root = bvh_build.run(); - - if (progress.get_cancel()) { - if (bvh2_root != NULL) { - bvh2_root->deleteSubtree(); - } - return; - } - - /* BVH builder returns tree in a binary mode (with two children per inner - * node. Need to adopt that for a wider BVH implementations. */ - BVHNode *root = widen_children_nodes(bvh2_root); - if (root != bvh2_root) { - bvh2_root->deleteSubtree(); - } - - if (progress.get_cancel()) { - if (root != NULL) { - root->deleteSubtree(); - } - return; - } - - /* pack triangles */ - progress.set_substatus("Packing BVH triangles and strands"); - pack_primitives(); - - if (progress.get_cancel()) { - root->deleteSubtree(); - return; - } - - /* pack nodes */ - progress.set_substatus("Packing BVH nodes"); - pack_nodes(root); - - /* free build nodes */ - root->deleteSubtree(); -} - -/* Refitting */ - -void BVH::refit(Progress &progress) -{ - progress.set_substatus("Packing BVH primitives"); - pack_primitives(); - - if (progress.get_cancel()) - return; - - progress.set_substatus("Refitting BVH nodes"); - refit_nodes(); -} - -void BVH::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility) -{ - /* Refit range of primitives. */ - for (int prim = start; prim < end; prim++) { - int pidx = pack.prim_index[prim]; - int tob = pack.prim_object[prim]; - Object *ob = objects[tob]; - - if (pidx == -1) { - /* Object instance. */ - bbox.grow(ob->bounds); - } - else { - /* Primitives. */ - if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) { - /* Curves. */ - const Hair *hair = static_cast<const Hair *>(ob->get_geometry()); - int prim_offset = (params.top_level) ? hair->prim_offset : 0; - Hair::Curve curve = hair->get_curve(pidx - prim_offset); - int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]); - - curve.bounds_grow(k, &hair->get_curve_keys()[0], &hair->get_curve_radius()[0], bbox); - - /* Motion curves. */ - if (hair->get_use_motion_blur()) { - Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if (attr) { - size_t hair_size = hair->get_curve_keys().size(); - size_t steps = hair->get_motion_steps() - 1; - float3 *key_steps = attr->data_float3(); - - for (size_t i = 0; i < steps; i++) - curve.bounds_grow(k, key_steps + i * hair_size, &hair->get_curve_radius()[0], bbox); - } - } - } - else { - /* Triangles. */ - const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry()); - int prim_offset = (params.top_level) ? mesh->prim_offset : 0; - Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset); - const float3 *vpos = &mesh->verts[0]; - - triangle.bounds_grow(vpos, bbox); - - /* Motion triangles. */ - if (mesh->use_motion_blur) { - Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if (attr) { - size_t mesh_size = mesh->verts.size(); - size_t steps = mesh->motion_steps - 1; - float3 *vert_steps = attr->data_float3(); - - for (size_t i = 0; i < steps; i++) - triangle.bounds_grow(vert_steps + i * mesh_size, bbox); - } - } - } - } - visibility |= ob->visibility_for_tracing(); - } -} - -/* Triangles */ - -void BVH::pack_triangle(int idx, float4 tri_verts[3]) -{ - int tob = pack.prim_object[idx]; - assert(tob >= 0 && tob < objects.size()); - const Mesh *mesh = static_cast<const Mesh *>(objects[tob]->get_geometry()); - - int tidx = pack.prim_index[idx]; - Mesh::Triangle t = mesh->get_triangle(tidx); - const float3 *vpos = &mesh->verts[0]; - float3 v0 = vpos[t.v[0]]; - float3 v1 = vpos[t.v[1]]; - float3 v2 = vpos[t.v[2]]; - - tri_verts[0] = float3_to_float4(v0); - tri_verts[1] = float3_to_float4(v1); - tri_verts[2] = float3_to_float4(v2); -} - -void BVH::pack_primitives() -{ - const size_t tidx_size = pack.prim_index.size(); - size_t num_prim_triangles = 0; - /* Count number of triangles primitives in BVH. */ - for (unsigned int i = 0; i < tidx_size; i++) { - if ((pack.prim_index[i] != -1)) { - if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { - ++num_prim_triangles; - } - } - } - /* Reserve size for arrays. */ - pack.prim_tri_index.clear(); - pack.prim_tri_index.resize(tidx_size); - pack.prim_tri_verts.clear(); - pack.prim_tri_verts.resize(num_prim_triangles * 3); - pack.prim_visibility.clear(); - pack.prim_visibility.resize(tidx_size); - /* Fill in all the arrays. */ - size_t prim_triangle_index = 0; - for (unsigned int i = 0; i < tidx_size; i++) { - if (pack.prim_index[i] != -1) { - int tob = pack.prim_object[i]; - Object *ob = objects[tob]; - if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { - pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]); - pack.prim_tri_index[i] = 3 * prim_triangle_index; - ++prim_triangle_index; - } - else { - pack.prim_tri_index[i] = -1; - } - pack.prim_visibility[i] = ob->visibility_for_tracing(); - } - else { - pack.prim_tri_index[i] = -1; - pack.prim_visibility[i] = 0; - } - } -} - -/* Pack Instances */ - -void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size) -{ - /* Adjust primitive index to point to the triangle in the global array, for - * geometry with transform applied and already in the top level BVH. - */ - for (size_t i = 0; i < pack.prim_index.size(); i++) { - if (pack.prim_index[i] != -1) { - pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset; - } - } - - /* track offsets of instanced BVH data in global array */ - size_t prim_offset = pack.prim_index.size(); - size_t nodes_offset = nodes_size; - size_t nodes_leaf_offset = leaf_nodes_size; - - /* clear array that gives the node indexes for instanced objects */ - pack.object_node.clear(); - - /* reserve */ - size_t prim_index_size = pack.prim_index.size(); - size_t prim_tri_verts_size = pack.prim_tri_verts.size(); - - size_t pack_prim_index_offset = prim_index_size; - size_t pack_prim_tri_verts_offset = prim_tri_verts_size; - size_t pack_nodes_offset = nodes_size; - size_t pack_leaf_nodes_offset = leaf_nodes_size; - size_t object_offset = 0; - - foreach (Geometry *geom, geometry) { - BVH *bvh = geom->bvh; - - if (geom->need_build_bvh(params.bvh_layout)) { - prim_index_size += bvh->pack.prim_index.size(); - prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); - nodes_size += bvh->pack.nodes.size(); - leaf_nodes_size += bvh->pack.leaf_nodes.size(); - } - } - - pack.prim_index.resize(prim_index_size); - pack.prim_type.resize(prim_index_size); - pack.prim_object.resize(prim_index_size); - pack.prim_visibility.resize(prim_index_size); - pack.prim_tri_verts.resize(prim_tri_verts_size); - pack.prim_tri_index.resize(prim_index_size); - pack.nodes.resize(nodes_size); - pack.leaf_nodes.resize(leaf_nodes_size); - pack.object_node.resize(objects.size()); - - if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0) { - pack.prim_time.resize(prim_index_size); - } - - int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL; - int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL; - int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL; - uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL; - float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL; - uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL; - int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL; - int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL; - float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL; - - map<Geometry *, int> geometry_map; - - /* merge */ - foreach (Object *ob, objects) { - Geometry *geom = ob->get_geometry(); - - /* We assume that if mesh doesn't need own BVH it was already included - * into a top-level BVH and no packing here is needed. - */ - if (!geom->need_build_bvh(params.bvh_layout)) { - pack.object_node[object_offset++] = 0; - continue; - } - - /* if mesh already added once, don't add it again, but used set - * node offset for this object */ - map<Geometry *, int>::iterator it = geometry_map.find(geom); - - if (geometry_map.find(geom) != geometry_map.end()) { - int noffset = it->second; - pack.object_node[object_offset++] = noffset; - continue; - } - - BVH *bvh = geom->bvh; - - int noffset = nodes_offset; - int noffset_leaf = nodes_leaf_offset; - int geom_prim_offset = geom->prim_offset; - - /* fill in node indexes for instances */ - if (bvh->pack.root_index == -1) - pack.object_node[object_offset++] = -noffset_leaf - 1; - else - pack.object_node[object_offset++] = noffset; - - geometry_map[geom] = pack.object_node[object_offset - 1]; - - /* merge primitive, object and triangle indexes */ - if (bvh->pack.prim_index.size()) { - size_t bvh_prim_index_size = bvh->pack.prim_index.size(); - int *bvh_prim_index = &bvh->pack.prim_index[0]; - int *bvh_prim_type = &bvh->pack.prim_type[0]; - uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0]; - uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0]; - float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL; - - for (size_t i = 0; i < bvh_prim_index_size; i++) { - if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { - pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_prim_index_offset] = -1; - } - else { - pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] + - pack_prim_tri_verts_offset; - } - - pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i]; - pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i]; - pack_prim_object[pack_prim_index_offset] = 0; // unused for instances - if (bvh_prim_time != NULL) { - pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i]; - } - pack_prim_index_offset++; - } - } - - /* Merge triangle vertices data. */ - if (bvh->pack.prim_tri_verts.size()) { - const size_t prim_tri_size = bvh->pack.prim_tri_verts.size(); - memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset, - &bvh->pack.prim_tri_verts[0], - prim_tri_size * sizeof(float4)); - pack_prim_tri_verts_offset += prim_tri_size; - } - - /* merge nodes */ - if (bvh->pack.leaf_nodes.size()) { - int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0]; - size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size(); - for (size_t i = 0, j = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE, j++) { - int4 data = leaf_nodes_offset[i]; - data.x += prim_offset; - data.y += prim_offset; - pack_leaf_nodes[pack_leaf_nodes_offset] = data; - for (int j = 1; j < BVH_NODE_LEAF_SIZE; ++j) { - pack_leaf_nodes[pack_leaf_nodes_offset + j] = leaf_nodes_offset[i + j]; - } - pack_leaf_nodes_offset += BVH_NODE_LEAF_SIZE; - } - } - - if (bvh->pack.nodes.size()) { - int4 *bvh_nodes = &bvh->pack.nodes[0]; - size_t bvh_nodes_size = bvh->pack.nodes.size(); - - for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) { - size_t nsize, nsize_bbox; - if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) { - nsize = BVH_UNALIGNED_NODE_SIZE; - nsize_bbox = 0; - } - else { - nsize = BVH_NODE_SIZE; - nsize_bbox = 0; - } - - memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox * sizeof(int4)); - - /* Modify offsets into arrays */ - int4 data = bvh_nodes[i + nsize_bbox]; - data.z += (data.z < 0) ? -noffset_leaf : noffset; - data.w += (data.w < 0) ? -noffset_leaf : noffset; - pack_nodes[pack_nodes_offset + nsize_bbox] = data; - - /* Usually this copies nothing, but we better - * be prepared for possible node size extension. - */ - memcpy(&pack_nodes[pack_nodes_offset + nsize_bbox + 1], - &bvh_nodes[i + nsize_bbox + 1], - sizeof(int4) * (nsize - (nsize_bbox + 1))); - - pack_nodes_offset += nsize; - i += nsize; - } - } - - nodes_offset += bvh->pack.nodes.size(); - nodes_leaf_offset += bvh->pack.leaf_nodes.size(); - prim_offset += bvh->pack.prim_index.size(); - } -} - CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h index 033b1fd8e04..94935c26f10 100644 --- a/intern/cycles/bvh/bvh.h +++ b/intern/cycles/bvh/bvh.h @@ -25,17 +25,16 @@ CCL_NAMESPACE_BEGIN -class Stats; -class Device; -class DeviceScene; +class BoundBox; class BVHNode; -struct BVHStackEntry; class BVHParams; -class BoundBox; -class LeafNode; +class Device; +class DeviceScene; class Geometry; +class LeafNode; class Object; class Progress; +class Stats; #define BVH_ALIGN 4096 #define TRI_NODE_SIZE 3 @@ -76,13 +75,10 @@ struct PackedBVH { } }; -enum BVH_TYPE { bvh2 }; - /* BVH */ class BVH { public: - PackedBVH pack; BVHParams params; vector<Geometry *> geometry; vector<Object *> objects; @@ -90,47 +86,15 @@ class BVH { static BVH *create(const BVHParams ¶ms, const vector<Geometry *> &geometry, const vector<Object *> &objects, - const Device *device); + Device *device); virtual ~BVH() { } - virtual void build(Progress &progress, Stats *stats = NULL); - virtual void copy_to_device(Progress & /*progress*/, DeviceScene * /*dscene*/) - { - } - - void refit(Progress &progress); - protected: BVH(const BVHParams ¶ms, const vector<Geometry *> &geometry, const vector<Object *> &objects); - - /* Refit range of primitives. */ - void refit_primitives(int start, int end, BoundBox &bbox, uint &visibility); - - /* triangles and strands */ - void pack_primitives(); - void pack_triangle(int idx, float4 storage[3]); - - /* merge instance BVH's */ - void pack_instances(size_t nodes_size, size_t leaf_nodes_size); - - /* for subclasses to implement */ - virtual void pack_nodes(const BVHNode *root) = 0; - virtual void refit_nodes() = 0; - - virtual BVHNode *widen_children_nodes(const BVHNode *root) = 0; -}; - -/* Pack Utility */ -struct BVHStackEntry { - const BVHNode *node; - int idx; - - BVHStackEntry(const BVHNode *n = 0, int i = 0); - int encodeIdx() const; }; CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh2.cpp b/intern/cycles/bvh/bvh2.cpp index c903070429e..379ae9b25ff 100644 --- a/intern/cycles/bvh/bvh2.cpp +++ b/intern/cycles/bvh/bvh2.cpp @@ -17,14 +17,28 @@ #include "bvh/bvh2.h" +#include "render/hair.h" #include "render/mesh.h" #include "render/object.h" +#include "bvh/bvh_build.h" #include "bvh/bvh_node.h" #include "bvh/bvh_unaligned.h" +#include "util/util_foreach.h" +#include "util/util_progress.h" + CCL_NAMESPACE_BEGIN +BVHStackEntry::BVHStackEntry(const BVHNode *n, int i) : node(n), idx(i) +{ +} + +int BVHStackEntry::encodeIdx() const +{ + return (node->is_leaf()) ? ~idx : idx; +} + BVH2::BVH2(const BVHParams ¶ms_, const vector<Geometry *> &geometry_, const vector<Object *> &objects_) @@ -32,6 +46,70 @@ BVH2::BVH2(const BVHParams ¶ms_, { } +void BVH2::build(Progress &progress, Stats *) +{ + progress.set_substatus("Building BVH"); + + /* build nodes */ + BVHBuild bvh_build(objects, + pack.prim_type, + pack.prim_index, + pack.prim_object, + pack.prim_time, + params, + progress); + BVHNode *bvh2_root = bvh_build.run(); + + if (progress.get_cancel()) { + if (bvh2_root != NULL) { + bvh2_root->deleteSubtree(); + } + return; + } + + /* BVH builder returns tree in a binary mode (with two children per inner + * node. Need to adopt that for a wider BVH implementations. */ + BVHNode *root = widen_children_nodes(bvh2_root); + if (root != bvh2_root) { + bvh2_root->deleteSubtree(); + } + + if (progress.get_cancel()) { + if (root != NULL) { + root->deleteSubtree(); + } + return; + } + + /* pack triangles */ + progress.set_substatus("Packing BVH triangles and strands"); + pack_primitives(); + + if (progress.get_cancel()) { + root->deleteSubtree(); + return; + } + + /* pack nodes */ + progress.set_substatus("Packing BVH nodes"); + pack_nodes(root); + + /* free build nodes */ + root->deleteSubtree(); +} + +void BVH2::refit(Progress &progress) +{ + progress.set_substatus("Packing BVH primitives"); + pack_primitives(); + + if (progress.get_cancel()) + return; + + progress.set_substatus("Refitting BVH nodes"); + refit_nodes(); +} + BVHNode *BVH2::widen_children_nodes(const BVHNode *root) { return const_cast<BVHNode *>(root); @@ -253,7 +331,7 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility) const int c0 = data[0].x; const int c1 = data[0].y; - BVH::refit_primitives(c0, c1, bbox, visibility); + refit_primitives(c0, c1, bbox, visibility); /* TODO(sergey): De-duplicate with pack_leaf(). */ float4 leaf_data[BVH_NODE_LEAF_SIZE]; @@ -292,4 +370,333 @@ void BVH2::refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility) } } +/* Refitting */ + +void BVH2::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility) +{ + /* Refit range of primitives. */ + for (int prim = start; prim < end; prim++) { + int pidx = pack.prim_index[prim]; + int tob = pack.prim_object[prim]; + Object *ob = objects[tob]; + + if (pidx == -1) { + /* Object instance. */ + bbox.grow(ob->bounds); + } + else { + /* Primitives. */ + if (pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) { + /* Curves. */ + const Hair *hair = static_cast<const Hair *>(ob->get_geometry()); + int prim_offset = (params.top_level) ? hair->prim_offset : 0; + Hair::Curve curve = hair->get_curve(pidx - prim_offset); + int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]); + + curve.bounds_grow(k, &hair->get_curve_keys()[0], &hair->get_curve_radius()[0], bbox); + + /* Motion curves. */ + if (hair->get_use_motion_blur()) { + Attribute *attr = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (attr) { + size_t hair_size = hair->get_curve_keys().size(); + size_t steps = hair->get_motion_steps() - 1; + float3 *key_steps = attr->data_float3(); + + for (size_t i = 0; i < steps; i++) + curve.bounds_grow(k, key_steps + i * hair_size, &hair->get_curve_radius()[0], bbox); + } + } + } + else { + /* Triangles. */ + const Mesh *mesh = static_cast<const Mesh *>(ob->get_geometry()); + int prim_offset = (params.top_level) ? mesh->prim_offset : 0; + Mesh::Triangle triangle = mesh->get_triangle(pidx - prim_offset); + const float3 *vpos = &mesh->verts[0]; + + triangle.bounds_grow(vpos, bbox); + + /* Motion triangles. */ + if (mesh->use_motion_blur) { + Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (attr) { + size_t mesh_size = mesh->verts.size(); + size_t steps = mesh->motion_steps - 1; + float3 *vert_steps = attr->data_float3(); + + for (size_t i = 0; i < steps; i++) + triangle.bounds_grow(vert_steps + i * mesh_size, bbox); + } + } + } + } + visibility |= ob->visibility_for_tracing(); + } +} + +/* Triangles */ + +void BVH2::pack_triangle(int idx, float4 tri_verts[3]) +{ + int tob = pack.prim_object[idx]; + assert(tob >= 0 && tob < objects.size()); + const Mesh *mesh = static_cast<const Mesh *>(objects[tob]->get_geometry()); + + int tidx = pack.prim_index[idx]; + Mesh::Triangle t = mesh->get_triangle(tidx); + const float3 *vpos = &mesh->verts[0]; + float3 v0 = vpos[t.v[0]]; + float3 v1 = vpos[t.v[1]]; + float3 v2 = vpos[t.v[2]]; + + tri_verts[0] = float3_to_float4(v0); + tri_verts[1] = float3_to_float4(v1); + tri_verts[2] = float3_to_float4(v2); +} + +void BVH2::pack_primitives() +{ + const size_t tidx_size = pack.prim_index.size(); + size_t num_prim_triangles = 0; + /* Count number of triangles primitives in BVH. */ + for (unsigned int i = 0; i < tidx_size; i++) { + if ((pack.prim_index[i] != -1)) { + if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { + ++num_prim_triangles; + } + } + } + /* Reserve size for arrays. */ + pack.prim_tri_index.clear(); + pack.prim_tri_index.resize(tidx_size); + pack.prim_tri_verts.clear(); + pack.prim_tri_verts.resize(num_prim_triangles * 3); + pack.prim_visibility.clear(); + pack.prim_visibility.resize(tidx_size); + /* Fill in all the arrays. */ + size_t prim_triangle_index = 0; + for (unsigned int i = 0; i < tidx_size; i++) { + if (pack.prim_index[i] != -1) { + int tob = pack.prim_object[i]; + Object *ob = objects[tob]; + if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { + pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]); + pack.prim_tri_index[i] = 3 * prim_triangle_index; + ++prim_triangle_index; + } + else { + pack.prim_tri_index[i] = -1; + } + pack.prim_visibility[i] = ob->visibility_for_tracing(); + } + else { + pack.prim_tri_index[i] = -1; + pack.prim_visibility[i] = 0; + } + } +} + +/* Pack Instances */ + +void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) +{ + /* Adjust primitive index to point to the triangle in the global array, for + * geometry with transform applied and already in the top level BVH. + */ + for (size_t i = 0; i < pack.prim_index.size(); i++) { + if (pack.prim_index[i] != -1) { + pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset; + } + } + + /* track offsets of instanced BVH data in global array */ + size_t prim_offset = pack.prim_index.size(); + size_t nodes_offset = nodes_size; + size_t nodes_leaf_offset = leaf_nodes_size; + + /* clear array that gives the node indexes for instanced objects */ + pack.object_node.clear(); + + /* reserve */ + size_t prim_index_size = pack.prim_index.size(); + size_t prim_tri_verts_size = pack.prim_tri_verts.size(); + + size_t pack_prim_index_offset = prim_index_size; + size_t pack_prim_tri_verts_offset = prim_tri_verts_size; + size_t pack_nodes_offset = nodes_size; + size_t pack_leaf_nodes_offset = leaf_nodes_size; + size_t object_offset = 0; + + foreach (Geometry *geom, geometry) { + BVH2 *bvh = static_cast<BVH2 *>(geom->bvh); + + if (geom->need_build_bvh(params.bvh_layout)) { + prim_index_size += bvh->pack.prim_index.size(); + prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); + nodes_size += bvh->pack.nodes.size(); + leaf_nodes_size += bvh->pack.leaf_nodes.size(); + } + } + + pack.prim_index.resize(prim_index_size); + pack.prim_type.resize(prim_index_size); + pack.prim_object.resize(prim_index_size); + pack.prim_visibility.resize(prim_index_size); + pack.prim_tri_verts.resize(prim_tri_verts_size); + pack.prim_tri_index.resize(prim_index_size); + pack.nodes.resize(nodes_size); + pack.leaf_nodes.resize(leaf_nodes_size); + pack.object_node.resize(objects.size()); + + if (params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0) { + pack.prim_time.resize(prim_index_size); + } + + int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL; + int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL; + int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL; + uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL; + float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL; + uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL; + int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL; + int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL; + float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL; + + unordered_map<Geometry *, int> geometry_map; + + /* merge */ + foreach (Object *ob, objects) { + Geometry *geom = ob->get_geometry(); + + /* We assume that if mesh doesn't need own BVH it was already included + * into a top-level BVH and no packing here is needed. + */ + if (!geom->need_build_bvh(params.bvh_layout)) { + pack.object_node[object_offset++] = 0; + continue; + } + + /* if mesh already added once, don't add it again, but used set + * node offset for this object */ + unordered_map<Geometry *, int>::iterator it = geometry_map.find(geom); + + if (geometry_map.find(geom) != geometry_map.end()) { + int noffset = it->second; + pack.object_node[object_offset++] = noffset; + continue; + } + + BVH2 *bvh = static_cast<BVH2 *>(geom->bvh); + + int noffset = nodes_offset; + int noffset_leaf = nodes_leaf_offset; + int geom_prim_offset = geom->prim_offset; + + /* fill in node indexes for instances */ + if (bvh->pack.root_index == -1) + pack.object_node[object_offset++] = -noffset_leaf - 1; + else + pack.object_node[object_offset++] = noffset; + + geometry_map[geom] = pack.object_node[object_offset - 1]; + + /* merge primitive, object and triangle indexes */ + if (bvh->pack.prim_index.size()) { + size_t bvh_prim_index_size = bvh->pack.prim_index.size(); + int *bvh_prim_index = &bvh->pack.prim_index[0]; + int *bvh_prim_type = &bvh->pack.prim_type[0]; + uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0]; + uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0]; + float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL; + + for (size_t i = 0; i < bvh_prim_index_size; i++) { + if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { + pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset; + pack_prim_tri_index[pack_prim_index_offset] = -1; + } + else { + pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset; + pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] + + pack_prim_tri_verts_offset; + } + + pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i]; + pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i]; + pack_prim_object[pack_prim_index_offset] = 0; // unused for instances + if (bvh_prim_time != NULL) { + pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i]; + } + pack_prim_index_offset++; + } + } + + /* Merge triangle vertices data. */ + if (bvh->pack.prim_tri_verts.size()) { + const size_t prim_tri_size = bvh->pack.prim_tri_verts.size(); + memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset, + &bvh->pack.prim_tri_verts[0], + prim_tri_size * sizeof(float4)); + pack_prim_tri_verts_offset += prim_tri_size; + } + + /* merge nodes */ + if (bvh->pack.leaf_nodes.size()) { + int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0]; + size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size(); + for (size_t i = 0, j = 0; i < leaf_nodes_offset_size; i += BVH_NODE_LEAF_SIZE, j++) { + int4 data = leaf_nodes_offset[i]; + data.x += prim_offset; + data.y += prim_offset; + pack_leaf_nodes[pack_leaf_nodes_offset] = data; + for (int j = 1; j < BVH_NODE_LEAF_SIZE; ++j) { + pack_leaf_nodes[pack_leaf_nodes_offset + j] = leaf_nodes_offset[i + j]; + } + pack_leaf_nodes_offset += BVH_NODE_LEAF_SIZE; + } + } + + if (bvh->pack.nodes.size()) { + int4 *bvh_nodes = &bvh->pack.nodes[0]; + size_t bvh_nodes_size = bvh->pack.nodes.size(); + + for (size_t i = 0, j = 0; i < bvh_nodes_size; j++) { + size_t nsize, nsize_bbox; + if (bvh_nodes[i].x & PATH_RAY_NODE_UNALIGNED) { + nsize = BVH_UNALIGNED_NODE_SIZE; + nsize_bbox = 0; + } + else { + nsize = BVH_NODE_SIZE; + nsize_bbox = 0; + } + + memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox * sizeof(int4)); + + /* Modify offsets into arrays */ + int4 data = bvh_nodes[i + nsize_bbox]; + data.z += (data.z < 0) ? -noffset_leaf : noffset; + data.w += (data.w < 0) ? -noffset_leaf : noffset; + pack_nodes[pack_nodes_offset + nsize_bbox] = data; + + /* Usually this copies nothing, but we better + * be prepared for possible node size extension. + */ + memcpy(&pack_nodes[pack_nodes_offset + nsize_bbox + 1], + &bvh_nodes[i + nsize_bbox + 1], + sizeof(int4) * (nsize - (nsize_bbox + 1))); + + pack_nodes_offset += nsize; + i += nsize; + } + } + + nodes_offset += bvh->pack.nodes.size(); + nodes_leaf_offset += bvh->pack.leaf_nodes.size(); + prim_offset += bvh->pack.prim_index.size(); + } +} + CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh2.h b/intern/cycles/bvh/bvh2.h index fa3e45b72d2..1030a0f76c7 100644 --- a/intern/cycles/bvh/bvh2.h +++ b/intern/cycles/bvh/bvh2.h @@ -26,23 +26,30 @@ CCL_NAMESPACE_BEGIN -class BVHNode; -struct BVHStackEntry; -class BVHParams; -class BoundBox; -class LeafNode; -class Object; -class Progress; - #define BVH_NODE_SIZE 4 #define BVH_NODE_LEAF_SIZE 1 #define BVH_UNALIGNED_NODE_SIZE 7 +/* Pack Utility */ +struct BVHStackEntry { + const BVHNode *node; + int idx; + + BVHStackEntry(const BVHNode *n = 0, int i = 0); + int encodeIdx() const; +}; + /* BVH2 * * Typical BVH with each node having two children. */ class BVH2 : public BVH { + public: + void build(Progress &progress, Stats *stats); + void refit(Progress &progress); + + PackedBVH pack; + protected: /* constructor */ friend class BVH; @@ -51,10 +58,10 @@ class BVH2 : public BVH { const vector<Object *> &objects); /* Building process. */ - virtual BVHNode *widen_children_nodes(const BVHNode *root) override; + virtual BVHNode *widen_children_nodes(const BVHNode *root); /* pack */ - void pack_nodes(const BVHNode *root) override; + void pack_nodes(const BVHNode *root); void pack_leaf(const BVHStackEntry &e, const LeafNode *leaf); void pack_inner(const BVHStackEntry &e, const BVHStackEntry &e0, const BVHStackEntry &e1); @@ -84,8 +91,18 @@ class BVH2 : public BVH { uint visibility1); /* refit */ - void refit_nodes() override; + void refit_nodes(); void refit_node(int idx, bool leaf, BoundBox &bbox, uint &visibility); + + /* Refit range of primitives. */ + void refit_primitives(int start, int end, BoundBox &bbox, uint &visibility); + + /* triangles and strands */ + void pack_primitives(); + void pack_triangle(int idx, float4 storage[3]); + + /* merge instance BVH's */ + void pack_instances(size_t nodes_size, size_t leaf_nodes_size); }; CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp index 910e3780b2e..b874bda7186 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/bvh_embree.cpp @@ -298,82 +298,31 @@ static bool rtc_progress_func(void *user_ptr, const double n) return !progress->get_cancel(); } -static size_t count_primitives(Geometry *geom) -{ - if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - Mesh *mesh = static_cast<Mesh *>(geom); - return mesh->num_triangles(); - } - else if (geom->geometry_type == Geometry::HAIR) { - Hair *hair = static_cast<Hair *>(geom); - return hair->num_segments(); - } - - return 0; -} - BVHEmbree::BVHEmbree(const BVHParams ¶ms_, const vector<Geometry *> &geometry_, - const vector<Object *> &objects_, - const Device *device) + const vector<Object *> &objects_) : BVH(params_, geometry_, objects_), scene(NULL), - mem_used(0), - top_level(NULL), - rtc_device((RTCDevice)device->bvh_device()), - stats(NULL), - curve_subdivisions(params.curve_subdivisions), - build_quality(RTC_BUILD_QUALITY_REFIT), - dynamic_scene(true) + rtc_device(NULL), + build_quality(RTC_BUILD_QUALITY_REFIT) { _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); - - rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL); - - pack.root_index = -1; } BVHEmbree::~BVHEmbree() { - if (!params.top_level) { - destroy(scene); - } -} - -void BVHEmbree::destroy(RTCScene scene) -{ if (scene) { rtcReleaseScene(scene); - scene = NULL; - } -} - -void BVHEmbree::delete_rtcScene() -{ - if (scene) { - /* When this BVH is used as an instance in a top level BVH, don't delete now - * Let the top_level BVH know that it should delete it later. */ - if (top_level) { - top_level->add_delayed_delete_scene(scene); - } - else { - rtcReleaseScene(scene); - if (delayed_delete_scenes.size()) { - foreach (RTCScene s, delayed_delete_scenes) { - rtcReleaseScene(s); - } - } - delayed_delete_scenes.clear(); - } - scene = NULL; } } -void BVHEmbree::build(Progress &progress, Stats *stats_) +void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_) { + rtc_device = rtc_device_; assert(rtc_device); - stats = stats_; + + rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL); rtcSetDeviceMemoryMonitorFunction(rtc_device, rtc_memory_monitor_func, stats); progress.set_substatus("Building BVH"); @@ -394,35 +343,7 @@ void BVHEmbree::build(Progress &progress, Stats *stats_) RTC_BUILD_QUALITY_MEDIUM); rtcSetSceneBuildQuality(scene, build_quality); - /* Count triangles and curves first, reserve arrays once. */ - size_t prim_count = 0; - - foreach (Object *ob, objects) { - if (params.top_level) { - if (!ob->is_traceable()) { - continue; - } - if (!ob->get_geometry()->is_instanced()) { - prim_count += count_primitives(ob->get_geometry()); - } - else { - ++prim_count; - } - } - else { - prim_count += count_primitives(ob->get_geometry()); - } - } - - pack.prim_object.reserve(prim_count); - pack.prim_type.reserve(prim_count); - pack.prim_index.reserve(prim_count); - pack.prim_tri_index.reserve(prim_count); - int i = 0; - - pack.object_node.clear(); - foreach (Object *ob, objects) { if (params.top_level) { if (!ob->is_traceable()) { @@ -445,37 +366,11 @@ void BVHEmbree::build(Progress &progress, Stats *stats_) } if (progress.get_cancel()) { - delete_rtcScene(); - stats = NULL; return; } rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress); rtcCommitScene(scene); - - pack_primitives(); - - if (progress.get_cancel()) { - delete_rtcScene(); - stats = NULL; - return; - } - - progress.set_substatus("Packing geometry"); - pack_nodes(NULL); - - stats = NULL; -} - -void BVHEmbree::copy_to_device(Progress & /*progress*/, DeviceScene *dscene) -{ - dscene->data.bvh.scene = scene; -} - -BVHNode *BVHEmbree::widen_children_nodes(const BVHNode * /*root*/) -{ - assert(!"Must not be called."); - return NULL; } void BVHEmbree::add_object(Object *ob, int i) @@ -498,15 +393,8 @@ void BVHEmbree::add_object(Object *ob, int i) void BVHEmbree::add_instance(Object *ob, int i) { - if (!ob || !ob->get_geometry()) { - assert(0); - return; - } BVHEmbree *instance_bvh = (BVHEmbree *)(ob->get_geometry()->bvh); - - if (instance_bvh->top_level != this) { - instance_bvh->top_level = this; - } + assert(instance_bvh != NULL); const size_t num_object_motion_steps = ob->use_motion() ? ob->get_motion().size() : 1; const size_t num_motion_steps = min(num_object_motion_steps, RTC_MAX_TIME_STEP_COUNT); @@ -538,11 +426,6 @@ void BVHEmbree::add_instance(Object *ob, int i) geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float *)&ob->get_tfm()); } - pack.prim_index.push_back_slow(-1); - pack.prim_object.push_back_slow(i); - pack.prim_type.push_back_slow(PRIMITIVE_NONE); - pack.prim_tri_index.push_back_slow(-1); - rtcSetGeometryUserData(geom_id, (void *)instance_bvh->scene); rtcSetGeometryMask(geom_id, ob->visibility_for_tracing()); @@ -553,20 +436,22 @@ void BVHEmbree::add_instance(Object *ob, int i) void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i) { - size_t prim_offset = pack.prim_index.size(); + size_t prim_offset = mesh->optix_prim_offset; + const Attribute *attr_mP = NULL; - size_t num_geometry_motion_steps = 1; + size_t num_motion_steps = 1; if (mesh->has_motion_blur()) { attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); if (attr_mP) { - num_geometry_motion_steps = mesh->get_motion_steps(); + num_motion_steps = mesh->get_motion_steps(); } } - const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT); - assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT); + assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT); + num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT); const size_t num_triangles = mesh->num_triangles(); + RTCGeometry geom_id = rtcNewGeometry(rtc_device, RTC_GEOMETRY_TYPE_TRIANGLE); rtcSetGeometryBuildQuality(geom_id, build_quality); rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); @@ -588,22 +473,6 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i) set_tri_vertex_buffer(geom_id, mesh, false); - size_t prim_object_size = pack.prim_object.size(); - pack.prim_object.resize(prim_object_size + num_triangles); - size_t prim_type_size = pack.prim_type.size(); - pack.prim_type.resize(prim_type_size + num_triangles); - size_t prim_index_size = pack.prim_index.size(); - pack.prim_index.resize(prim_index_size + num_triangles); - pack.prim_tri_index.resize(prim_index_size + num_triangles); - int prim_type = (num_motion_steps > 1 ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE); - - for (size_t j = 0; j < num_triangles; ++j) { - pack.prim_object[prim_object_size + j] = i; - pack.prim_type[prim_type_size + j] = prim_type; - pack.prim_index[prim_index_size + j] = j; - pack.prim_tri_index[prim_index_size + j] = j; - } - rtcSetGeometryUserData(geom_id, (void *)prim_offset); rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); rtcSetGeometryMask(geom_id, ob->visibility_for_tracing()); @@ -629,12 +498,12 @@ void BVHEmbree::set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, con } } } - const size_t num_verts = mesh->verts.size(); + const size_t num_verts = mesh->get_verts().size(); for (int t = 0; t < num_motion_steps; ++t) { const float3 *verts; if (t == t_mid) { - verts = &mesh->verts[0]; + verts = mesh->get_verts().data(); } else { int t_ = (t > t_mid) ? (t - 1) : t; @@ -736,24 +605,19 @@ void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, c void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) { - size_t prim_offset = pack.prim_index.size(); + size_t prim_offset = hair->optix_prim_offset; + const Attribute *attr_mP = NULL; - size_t num_geometry_motion_steps = 1; + size_t num_motion_steps = 1; if (hair->has_motion_blur()) { attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); if (attr_mP) { - num_geometry_motion_steps = hair->get_motion_steps(); + num_motion_steps = hair->get_motion_steps(); } } - const size_t num_motion_steps = min(num_geometry_motion_steps, RTC_MAX_TIME_STEP_COUNT); - const PrimitiveType primitive_type = - (num_motion_steps > 1) ? - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : - PRIMITIVE_MOTION_CURVE_THICK) : - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK); - - assert(num_geometry_motion_steps <= RTC_MAX_TIME_STEP_COUNT); + assert(num_motion_steps <= RTC_MAX_TIME_STEP_COUNT); + num_motion_steps = min(num_motion_steps, RTC_MAX_TIME_STEP_COUNT); const size_t num_curves = hair->num_curves(); size_t num_segments = 0; @@ -763,22 +627,12 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) num_segments += c.num_segments(); } - /* Make room for Cycles specific data. */ - size_t prim_object_size = pack.prim_object.size(); - pack.prim_object.resize(prim_object_size + num_segments); - size_t prim_type_size = pack.prim_type.size(); - pack.prim_type.resize(prim_type_size + num_segments); - size_t prim_index_size = pack.prim_index.size(); - pack.prim_index.resize(prim_index_size + num_segments); - size_t prim_tri_index_size = pack.prim_index.size(); - pack.prim_tri_index.resize(prim_tri_index_size + num_segments); - enum RTCGeometryType type = (hair->curve_shape == CURVE_RIBBON ? RTC_GEOMETRY_TYPE_FLAT_CATMULL_ROM_CURVE : RTC_GEOMETRY_TYPE_ROUND_CATMULL_ROM_CURVE); RTCGeometry geom_id = rtcNewGeometry(rtc_device, type); - rtcSetGeometryTessellationRate(geom_id, curve_subdivisions + 1); + rtcSetGeometryTessellationRate(geom_id, params.curve_subdivisions + 1); unsigned *rtc_indices = (unsigned *)rtcSetNewGeometryBuffer( geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof(int), num_segments); size_t rtc_index = 0; @@ -788,11 +642,6 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) rtc_indices[rtc_index] = c.first_key + k; /* Room for extra CVs at Catmull-Rom splines. */ rtc_indices[rtc_index] += j * 2; - /* Cycles specific data. */ - pack.prim_object[prim_object_size + rtc_index] = i; - pack.prim_type[prim_type_size + rtc_index] = (PRIMITIVE_PACK_SEGMENT(primitive_type, k)); - pack.prim_index[prim_index_size + rtc_index] = j; - pack.prim_tri_index[prim_tri_index_size + rtc_index] = rtc_index; ++rtc_index; } @@ -818,134 +667,10 @@ void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) rtcReleaseGeometry(geom_id); } -void BVHEmbree::pack_nodes(const BVHNode *) +void BVHEmbree::refit(Progress &progress) { - /* Quite a bit of this code is for compatibility with Cycles' native BVH. */ - if (!params.top_level) { - return; - } - - for (size_t i = 0; i < pack.prim_index.size(); ++i) { - if (pack.prim_index[i] != -1) { - pack.prim_index[i] += objects[pack.prim_object[i]]->get_geometry()->prim_offset; - } - } - - size_t prim_offset = pack.prim_index.size(); - - /* reserve */ - size_t prim_index_size = pack.prim_index.size(); - size_t prim_tri_verts_size = pack.prim_tri_verts.size(); - - size_t pack_prim_index_offset = prim_index_size; - size_t pack_prim_tri_verts_offset = prim_tri_verts_size; - size_t object_offset = 0; - - map<Geometry *, int> geometry_map; - - foreach (Object *ob, objects) { - Geometry *geom = ob->get_geometry(); - BVH *bvh = geom->bvh; - - if (geom->need_build_bvh(BVH_LAYOUT_EMBREE)) { - if (geometry_map.find(geom) == geometry_map.end()) { - prim_index_size += bvh->pack.prim_index.size(); - prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); - geometry_map[geom] = 1; - } - } - } + progress.set_substatus("Refitting BVH nodes"); - geometry_map.clear(); - - pack.prim_index.resize(prim_index_size); - pack.prim_type.resize(prim_index_size); - pack.prim_object.resize(prim_index_size); - pack.prim_visibility.clear(); - pack.prim_tri_verts.resize(prim_tri_verts_size); - pack.prim_tri_index.resize(prim_index_size); - pack.object_node.resize(objects.size()); - - int *pack_prim_index = (pack.prim_index.size()) ? &pack.prim_index[0] : NULL; - int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL; - int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL; - float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL; - uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL; - - /* merge */ - foreach (Object *ob, objects) { - Geometry *geom = ob->get_geometry(); - - /* We assume that if mesh doesn't need own BVH it was already included - * into a top-level BVH and no packing here is needed. - */ - if (!geom->need_build_bvh(BVH_LAYOUT_EMBREE)) { - pack.object_node[object_offset++] = prim_offset; - continue; - } - - /* if geom already added once, don't add it again, but used set - * node offset for this object */ - map<Geometry *, int>::iterator it = geometry_map.find(geom); - - if (geometry_map.find(geom) != geometry_map.end()) { - int noffset = it->second; - pack.object_node[object_offset++] = noffset; - continue; - } - - BVHEmbree *bvh = (BVHEmbree *)geom->bvh; - - rtc_memory_monitor_func(stats, unaccounted_mem, true); - unaccounted_mem = 0; - - int geom_prim_offset = geom->prim_offset; - - /* fill in node indexes for instances */ - pack.object_node[object_offset++] = prim_offset; - - geometry_map[geom] = pack.object_node[object_offset - 1]; - - /* merge primitive, object and triangle indexes */ - if (bvh->pack.prim_index.size()) { - size_t bvh_prim_index_size = bvh->pack.prim_index.size(); - int *bvh_prim_index = &bvh->pack.prim_index[0]; - int *bvh_prim_type = &bvh->pack.prim_type[0]; - uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0]; - - for (size_t i = 0; i < bvh_prim_index_size; ++i) { - if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { - pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_prim_index_offset] = -1; - } - else { - pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] + - pack_prim_tri_verts_offset; - } - - pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i]; - pack_prim_object[pack_prim_index_offset] = 0; - - ++pack_prim_index_offset; - } - } - - /* Merge triangle vertices data. */ - if (bvh->pack.prim_tri_verts.size()) { - const size_t prim_tri_size = bvh->pack.prim_tri_verts.size(); - memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset, - &bvh->pack.prim_tri_verts[0], - prim_tri_size * sizeof(float4)); - pack_prim_tri_verts_offset += prim_tri_size; - } - - prim_offset += bvh->pack.prim_index.size(); - } -} - -void BVHEmbree::refit_nodes() -{ /* Update all vertex buffers, then tell Embree to rebuild/-fit the BVHs. */ unsigned geom_id = 0; foreach (Object *ob, objects) { @@ -971,8 +696,10 @@ void BVHEmbree::refit_nodes() } geom_id += 2; } + rtcCommitScene(scene); } + CCL_NAMESPACE_END #endif /* WITH_EMBREE */ diff --git a/intern/cycles/bvh/bvh_embree.h b/intern/cycles/bvh/bvh_embree.h index 3e895e7b588..01636fbd1dc 100644 --- a/intern/cycles/bvh/bvh_embree.h +++ b/intern/cycles/bvh/bvh_embree.h @@ -31,56 +31,34 @@ CCL_NAMESPACE_BEGIN -class Geometry; class Hair; class Mesh; class BVHEmbree : public BVH { public: - virtual void build(Progress &progress, Stats *stats) override; - virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override; - virtual ~BVHEmbree(); - RTCScene scene; - static void destroy(RTCScene); + void build(Progress &progress, Stats *stats, RTCDevice rtc_device); + void refit(Progress &progress); - /* Building process. */ - virtual BVHNode *widen_children_nodes(const BVHNode *root) override; + RTCScene scene; protected: friend class BVH; BVHEmbree(const BVHParams ¶ms, const vector<Geometry *> &geometry, - const vector<Object *> &objects, - const Device *device); - - virtual void pack_nodes(const BVHNode *) override; - virtual void refit_nodes() override; + const vector<Object *> &objects); + virtual ~BVHEmbree(); void add_object(Object *ob, int i); void add_instance(Object *ob, int i); void add_curves(const Object *ob, const Hair *hair, int i); void add_triangles(const Object *ob, const Mesh *mesh, int i); - ssize_t mem_used; - - void add_delayed_delete_scene(RTCScene scene) - { - delayed_delete_scenes.push_back(scene); - } - BVHEmbree *top_level; - private: - void delete_rtcScene(); void set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, const bool update); void set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, const bool update); RTCDevice rtc_device; - - Stats *stats; - vector<RTCScene> delayed_delete_scenes; - int curve_subdivisions; enum RTCBuildQuality build_quality; - bool dynamic_scene; }; CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh_multi.cpp b/intern/cycles/bvh/bvh_multi.cpp new file mode 100644 index 00000000000..a9e771f20f1 --- /dev/null +++ b/intern/cycles/bvh/bvh_multi.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2020, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bvh/bvh_multi.h" + +#include "util/util_foreach.h" + +CCL_NAMESPACE_BEGIN + +BVHMulti::BVHMulti(const BVHParams ¶ms_, + const vector<Geometry *> &geometry_, + const vector<Object *> &objects_) + : BVH(params_, geometry_, objects_) +{ +} + +BVHMulti::~BVHMulti() +{ + foreach (BVH *bvh, sub_bvhs) { + delete bvh; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh_multi.h b/intern/cycles/bvh/bvh_multi.h new file mode 100644 index 00000000000..840438c5d0c --- /dev/null +++ b/intern/cycles/bvh/bvh_multi.h @@ -0,0 +1,39 @@ +/* + * Copyright 2020, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BVH_MULTI_H__ +#define __BVH_MULTI_H__ + +#include "bvh/bvh.h" +#include "bvh/bvh_params.h" + +CCL_NAMESPACE_BEGIN + +class BVHMulti : public BVH { + public: + vector<BVH *> sub_bvhs; + + protected: + friend class BVH; + BVHMulti(const BVHParams ¶ms, + const vector<Geometry *> &geometry, + const vector<Object *> &objects); + virtual ~BVHMulti(); +}; + +CCL_NAMESPACE_END + +#endif /* __BVH_MULTI_H__ */ diff --git a/intern/cycles/bvh/bvh_optix.cpp b/intern/cycles/bvh/bvh_optix.cpp index 52fc9c0a50d..e094f339ede 100644 --- a/intern/cycles/bvh/bvh_optix.cpp +++ b/intern/cycles/bvh/bvh_optix.cpp @@ -19,212 +19,22 @@ # include "bvh/bvh_optix.h" -# include "device/device.h" - -# include "render/geometry.h" -# include "render/hair.h" -# include "render/mesh.h" -# include "render/object.h" - -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_progress.h" - CCL_NAMESPACE_BEGIN BVHOptiX::BVHOptiX(const BVHParams ¶ms_, const vector<Geometry *> &geometry_, - const vector<Object *> &objects_) - : BVH(params_, geometry_, objects_) + const vector<Object *> &objects_, + Device *device) + : BVH(params_, geometry_, objects_), + traversable_handle(0), + as_data(device, params_.top_level ? "optix tlas" : "optix blas"), + motion_transform_data(device, "optix motion transform") { - optix_handle = 0; - optix_data_handle = 0; - do_refit = false; } BVHOptiX::~BVHOptiX() { -} - -void BVHOptiX::build(Progress &, Stats *) -{ - if (params.top_level) - pack_tlas(); - else - pack_blas(); -} - -void BVHOptiX::copy_to_device(Progress &progress, DeviceScene *dscene) -{ - progress.set_status("Updating Scene BVH", "Building OptiX acceleration structure"); - - Device *const device = dscene->bvh_nodes.device; - if (!device->build_optix_bvh(this)) - progress.set_error("Failed to build OptiX acceleration structure"); -} - -void BVHOptiX::pack_blas() -{ - // Bottom-level BVH can contain multiple primitive types, so merge them: - assert(geometry.size() == 1 && objects.size() == 1); // These are built per-mesh - Geometry *const geom = geometry[0]; - - if (geom->geometry_type == Geometry::HAIR) { - Hair *const hair = static_cast<Hair *const>(geom); - if (hair->num_curves() > 0) { - const size_t num_curves = hair->num_curves(); - const size_t num_segments = hair->num_segments(); - pack.prim_type.reserve(pack.prim_type.size() + num_segments); - pack.prim_index.reserve(pack.prim_index.size() + num_segments); - pack.prim_object.reserve(pack.prim_object.size() + num_segments); - // 'pack.prim_time' is only used in geom_curve_intersect.h - // It is not needed because of OPTIX_MOTION_FLAG_[START|END]_VANISH - - uint type = (hair->get_use_motion_blur() && - hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) ? - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : - PRIMITIVE_MOTION_CURVE_THICK) : - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : - PRIMITIVE_CURVE_THICK); - - for (size_t j = 0; j < num_curves; ++j) { - const Hair::Curve curve = hair->get_curve(j); - for (size_t k = 0; k < curve.num_segments(); ++k) { - pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k)); - // Each curve segment points back to its curve index - pack.prim_index.push_back_reserved(j); - pack.prim_object.push_back_reserved(0); - } - } - } - } - else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - Mesh *const mesh = static_cast<Mesh *const>(geom); - if (mesh->num_triangles() > 0) { - const size_t num_triangles = mesh->num_triangles(); - pack.prim_type.reserve(pack.prim_type.size() + num_triangles); - pack.prim_index.reserve(pack.prim_index.size() + num_triangles); - pack.prim_object.reserve(pack.prim_object.size() + num_triangles); - - uint type = PRIMITIVE_TRIANGLE; - if (mesh->get_use_motion_blur() && mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) - type = PRIMITIVE_MOTION_TRIANGLE; - - for (size_t k = 0; k < num_triangles; ++k) { - pack.prim_type.push_back_reserved(type); - pack.prim_index.push_back_reserved(k); - pack.prim_object.push_back_reserved(0); - } - } - } - - // Initialize visibility to zero and later update it during top-level build - uint prev_visibility = objects[0]->get_visibility(); - objects[0]->set_visibility(0); - - // Update 'pack.prim_tri_index', 'pack.prim_tri_verts' and 'pack.prim_visibility' - pack_primitives(); - - // Reset visibility after packing - objects[0]->set_visibility(prev_visibility); -} - -void BVHOptiX::pack_tlas() -{ - // Calculate total packed size - size_t prim_index_size = 0; - size_t prim_tri_verts_size = 0; - foreach (Geometry *geom, geometry) { - BVH *const bvh = geom->bvh; - prim_index_size += bvh->pack.prim_index.size(); - prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); - } - - if (prim_index_size == 0) - return; // Abort right away if this is an empty BVH - - size_t pack_offset = 0; - size_t pack_verts_offset = 0; - - pack.prim_type.resize(prim_index_size); - int *pack_prim_type = pack.prim_type.data(); - pack.prim_index.resize(prim_index_size); - int *pack_prim_index = pack.prim_index.data(); - pack.prim_object.resize(prim_index_size); - int *pack_prim_object = pack.prim_object.data(); - pack.prim_visibility.resize(prim_index_size); - uint *pack_prim_visibility = pack.prim_visibility.data(); - pack.prim_tri_index.resize(prim_index_size); - uint *pack_prim_tri_index = pack.prim_tri_index.data(); - pack.prim_tri_verts.resize(prim_tri_verts_size); - float4 *pack_prim_tri_verts = pack.prim_tri_verts.data(); - - // Top-level BVH should only contain instances, see 'Geometry::need_build_bvh' - // Iterate over scene mesh list instead of objects, since the 'prim_offset' is calculated based - // on that list, which may be ordered differently from the object list. - foreach (Geometry *geom, geometry) { - PackedBVH &bvh_pack = geom->bvh->pack; - int geom_prim_offset = geom->prim_offset; - - // Merge visibility flags of all objects and fix object indices for non-instanced geometry - int object_index = 0; // Unused for instanced geometry - int object_visibility = 0; - foreach (Object *ob, objects) { - if (ob->get_geometry() == geom) { - object_visibility |= ob->visibility_for_tracing(); - if (!geom->is_instanced()) { - object_index = ob->get_device_index(); - break; - } - } - } - - // Merge primitive, object and triangle indexes - if (!bvh_pack.prim_index.empty()) { - int *bvh_prim_type = &bvh_pack.prim_type[0]; - int *bvh_prim_index = &bvh_pack.prim_index[0]; - uint *bvh_prim_tri_index = &bvh_pack.prim_tri_index[0]; - uint *bvh_prim_visibility = &bvh_pack.prim_visibility[0]; - - for (size_t i = 0; i < bvh_pack.prim_index.size(); i++, pack_offset++) { - if (bvh_pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { - pack_prim_index[pack_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_offset] = -1; - } - else { - pack_prim_index[pack_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_offset] = bvh_prim_tri_index[i] + pack_verts_offset; - } - - pack_prim_type[pack_offset] = bvh_prim_type[i]; - pack_prim_object[pack_offset] = object_index; - pack_prim_visibility[pack_offset] = bvh_prim_visibility[i] | object_visibility; - } - } - - // Merge triangle vertex data - if (!bvh_pack.prim_tri_verts.empty()) { - const size_t prim_tri_size = bvh_pack.prim_tri_verts.size(); - memcpy(pack_prim_tri_verts + pack_verts_offset, - bvh_pack.prim_tri_verts.data(), - prim_tri_size * sizeof(float4)); - pack_verts_offset += prim_tri_size; - } - } -} - -void BVHOptiX::pack_nodes(const BVHNode *) -{ -} - -void BVHOptiX::refit_nodes() -{ - do_refit = true; -} - -BVHNode *BVHOptiX::widen_children_nodes(const BVHNode *) -{ - return NULL; + // Acceleration structure memory is freed via the 'as_data' destructor } CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/bvh_optix.h b/intern/cycles/bvh/bvh_optix.h index 663cba67260..aa514beae0d 100644 --- a/intern/cycles/bvh/bvh_optix.h +++ b/intern/cycles/bvh/bvh_optix.h @@ -26,33 +26,19 @@ CCL_NAMESPACE_BEGIN -class Geometry; -class Optix; - class BVHOptiX : public BVH { - friend class BVH; - public: - uint64_t optix_handle; - uint64_t optix_data_handle; - bool do_refit; + uint64_t traversable_handle; + device_only_memory<char> as_data; + device_only_memory<char> motion_transform_data; + protected: + friend class BVH; BVHOptiX(const BVHParams ¶ms, const vector<Geometry *> &geometry, - const vector<Object *> &objects); + const vector<Object *> &objects, + Device *device); virtual ~BVHOptiX(); - - virtual void build(Progress &progress, Stats *) override; - virtual void copy_to_device(Progress &progress, DeviceScene *dscene) override; - - private: - void pack_blas(); - void pack_tlas(); - - virtual void pack_nodes(const BVHNode *) override; - virtual void refit_nodes() override; - - virtual BVHNode *widen_children_nodes(const BVHNode *) override; }; CCL_NAMESPACE_END diff --git a/intern/cycles/device/cuda/device_cuda.h b/intern/cycles/device/cuda/device_cuda.h index e5e3e24165d..c3271c3cfcf 100644 --- a/intern/cycles/device/cuda/device_cuda.h +++ b/intern/cycles/device/cuda/device_cuda.h @@ -71,6 +71,7 @@ class CUDADevice : public Device { }; typedef map<device_memory *, CUDAMem> CUDAMemMap; CUDAMemMap cuda_mem_map; + thread_mutex cuda_mem_map_mutex; struct PixelMem { GLuint cuPBO; diff --git a/intern/cycles/device/cuda/device_cuda_impl.cpp b/intern/cycles/device/cuda/device_cuda_impl.cpp index 01adf10f252..48151365e5d 100644 --- a/intern/cycles/device/cuda/device_cuda_impl.cpp +++ b/intern/cycles/device/cuda/device_cuda_impl.cpp @@ -718,8 +718,10 @@ void CUDADevice::init_host_memory() void CUDADevice::load_texture_info() { if (need_texture_info) { - texture_info.copy_to_device(); + /* Unset flag before copying, so this does not loop indefinitely if the copy below calls + * into 'move_textures_to_host' (which calls 'load_texture_info' again). */ need_texture_info = false; + texture_info.copy_to_device(); } } @@ -988,6 +990,7 @@ void CUDADevice::mem_alloc(device_memory &mem) assert(!"mem_alloc not supported for global memory."); } else { + thread_scoped_lock lock(cuda_mem_map_mutex); generic_alloc(mem); } } @@ -1006,10 +1009,10 @@ void CUDADevice::mem_copy_to(device_memory &mem) tex_alloc((device_texture &)mem); } else { + thread_scoped_lock lock(cuda_mem_map_mutex); if (!mem.device_pointer) { generic_alloc(mem); } - generic_copy_to(mem); } } @@ -1048,6 +1051,7 @@ void CUDADevice::mem_zero(device_memory &mem) /* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory * regardless of mem.host_pointer and mem.shared_pointer. */ + thread_scoped_lock lock(cuda_mem_map_mutex); if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { const CUDAContextScope scope(this); cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size())); @@ -1069,6 +1073,7 @@ void CUDADevice::mem_free(device_memory &mem) tex_free((device_texture &)mem); } else { + thread_scoped_lock lock(cuda_mem_map_mutex); generic_free(mem); } } @@ -1092,6 +1097,7 @@ void CUDADevice::const_copy_to(const char *name, void *host, size_t size) void CUDADevice::global_alloc(device_memory &mem) { if (mem.is_resident(this)) { + thread_scoped_lock lock(cuda_mem_map_mutex); generic_alloc(mem); generic_copy_to(mem); } @@ -1102,6 +1108,7 @@ void CUDADevice::global_alloc(device_memory &mem) void CUDADevice::global_free(device_memory &mem) { if (mem.is_resident(this) && mem.device_pointer) { + thread_scoped_lock lock(cuda_mem_map_mutex); generic_free(mem); } } @@ -1170,6 +1177,8 @@ void CUDADevice::tex_alloc(device_texture &mem) size_t src_pitch = mem.data_width * dsize * mem.data_elements; size_t dst_pitch = src_pitch; + thread_scoped_lock lock(cuda_mem_map_mutex); + if (!mem.is_resident(this)) { cmem = &cuda_mem_map[&mem]; cmem->texobject = 0; @@ -1257,6 +1266,9 @@ void CUDADevice::tex_alloc(device_texture &mem) cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size)); } + /* Unlock mutex before resizing texture info, since that may attempt to lock it again. */ + lock.unlock(); + /* Resize once */ const uint slot = mem.slot; if (slot >= texture_info.size()) { @@ -1305,6 +1317,11 @@ void CUDADevice::tex_alloc(device_texture &mem) texDesc.filterMode = filter_mode; texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; + /* Lock again and refresh the data pointer (in case another thread modified the map in the + * meantime). */ + lock.lock(); + cmem = &cuda_mem_map[&mem]; + cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL)); texture_info[slot].data = (uint64_t)cmem->texobject; @@ -1318,6 +1335,7 @@ void CUDADevice::tex_free(device_texture &mem) { if (mem.device_pointer) { CUDAContextScope scope(this); + thread_scoped_lock lock(cuda_mem_map_mutex); const CUDAMem &cmem = cuda_mem_map[&mem]; if (cmem.texobject) { diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index eb8fb8040e3..1efd628b79b 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -17,6 +17,8 @@ #include <stdlib.h> #include <string.h> +#include "bvh/bvh2.h" + #include "device/device.h" #include "device/device_intern.h" @@ -364,6 +366,19 @@ void Device::draw_pixels(device_memory &rgba, } } +void Device::build_bvh(BVH *bvh, Progress &progress, bool refit) +{ + assert(bvh->params.bvh_layout == BVH_LAYOUT_BVH2); + + BVH2 *const bvh2 = static_cast<BVH2 *>(bvh); + if (refit) { + bvh2->refit(progress); + } + else { + bvh2->build(progress, &stats); + } +} + Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) { #ifdef WITH_MULTI diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index 2006db02ce7..e9b7cde7a16 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -373,12 +373,6 @@ class Device { return NULL; } - /* Device specific pointer for BVH creation. Currently only used by Embree. */ - virtual void *bvh_device() const - { - return NULL; - } - /* load/compile kernels, must be called before adding tasks */ virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/) { @@ -427,10 +421,7 @@ class Device { const DeviceDrawParams &draw_params); /* acceleration structure building */ - virtual bool build_optix_bvh(BVH *) - { - return false; - } + virtual void build_bvh(BVH *bvh, Progress &progress, bool refit); #ifdef WITH_NETWORK /* networking */ diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp index 6912ac1e638..fea4fc53d1f 100644 --- a/intern/cycles/device/device_cpu.cpp +++ b/intern/cycles/device/device_cpu.cpp @@ -47,6 +47,8 @@ #include "kernel/osl/osl_globals.h" // clang-format on +#include "bvh/bvh_embree.h" + #include "render/buffers.h" #include "render/coverage.h" @@ -188,6 +190,7 @@ class CPUDevice : public Device { #endif thread_spin_lock oidn_task_lock; #ifdef WITH_EMBREE + RTCScene embree_scene = NULL; RTCDevice embree_device; #endif @@ -472,6 +475,15 @@ class CPUDevice : public Device { virtual void const_copy_to(const char *name, void *host, size_t size) override { +#if WITH_EMBREE + if (strcmp(name, "__data") == 0) { + assert(size <= sizeof(KernelData)); + + // Update scene handle (since it is different for each device on multi devices) + KernelData *const data = (KernelData *)host; + data->bvh.scene = embree_scene; + } +#endif kernel_const_copy(&kernel_globals, name, host, size); } @@ -537,13 +549,26 @@ class CPUDevice : public Device { #endif } - void *bvh_device() const override + void build_bvh(BVH *bvh, Progress &progress, bool refit) override { #ifdef WITH_EMBREE - return embree_device; -#else - return NULL; + if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE || + bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) { + BVHEmbree *const bvh_embree = static_cast<BVHEmbree *>(bvh); + if (refit) { + bvh_embree->refit(progress); + } + else { + bvh_embree->build(progress, &stats, embree_device); + } + + if (bvh->params.top_level) { + embree_scene = bvh_embree->scene; + } + } + else #endif + Device::build_bvh(bvh, progress, refit); } void thread_run(DeviceTask &task) diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp index 2e72a0b4393..e5b138917ff 100644 --- a/intern/cycles/device/device_multi.cpp +++ b/intern/cycles/device/device_multi.cpp @@ -17,11 +17,14 @@ #include <sstream> #include <stdlib.h> +#include "bvh/bvh_multi.h" + #include "device/device.h" #include "device/device_intern.h" #include "device/device_network.h" #include "render/buffers.h" +#include "render/geometry.h" #include "util/util_foreach.h" #include "util/util_list.h" @@ -141,7 +144,7 @@ class MultiDevice : public Device { delete sub.device; } - const string &error_message() + const string &error_message() override { error_msg.clear(); @@ -153,7 +156,7 @@ class MultiDevice : public Device { return error_msg; } - virtual bool show_samples() const + virtual bool show_samples() const override { if (devices.size() > 1) { return false; @@ -161,16 +164,31 @@ class MultiDevice : public Device { return devices.front().device->show_samples(); } - virtual BVHLayoutMask get_bvh_layout_mask() const + virtual BVHLayoutMask get_bvh_layout_mask() const override { BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL; + BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE; foreach (const SubDevice &sub_device, devices) { - bvh_layout_mask &= sub_device.device->get_bvh_layout_mask(); + BVHLayoutMask device_bvh_layout_mask = sub_device.device->get_bvh_layout_mask(); + bvh_layout_mask &= device_bvh_layout_mask; + bvh_layout_mask_all |= device_bvh_layout_mask; + } + + /* With multiple OptiX devices, every device needs its own acceleration structure */ + if (bvh_layout_mask == BVH_LAYOUT_OPTIX) { + return BVH_LAYOUT_MULTI_OPTIX; } + + /* When devices do not share a common BVH layout, fall back to creating one for each */ + const BVHLayoutMask BVH_LAYOUT_OPTIX_EMBREE = (BVH_LAYOUT_OPTIX | BVH_LAYOUT_EMBREE); + if ((bvh_layout_mask_all & BVH_LAYOUT_OPTIX_EMBREE) == BVH_LAYOUT_OPTIX_EMBREE) { + return BVH_LAYOUT_MULTI_OPTIX_EMBREE; + } + return bvh_layout_mask; } - bool load_kernels(const DeviceRequestedFeatures &requested_features) + bool load_kernels(const DeviceRequestedFeatures &requested_features) override { foreach (SubDevice &sub, devices) if (!sub.device->load_kernels(requested_features)) @@ -188,7 +206,7 @@ class MultiDevice : public Device { return true; } - bool wait_for_availability(const DeviceRequestedFeatures &requested_features) + bool wait_for_availability(const DeviceRequestedFeatures &requested_features) override { foreach (SubDevice &sub, devices) if (!sub.device->wait_for_availability(requested_features)) @@ -203,7 +221,7 @@ class MultiDevice : public Device { return true; } - DeviceKernelStatus get_active_kernel_switch_state() + DeviceKernelStatus get_active_kernel_switch_state() override { DeviceKernelStatus result = DEVICE_KERNEL_USING_FEATURE_KERNEL; @@ -227,24 +245,61 @@ class MultiDevice : public Device { return result; } - bool build_optix_bvh(BVH *bvh) + void build_bvh(BVH *bvh, Progress &progress, bool refit) override { + /* Try to build and share a single acceleration structure, if possible */ + if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2) { + devices.back().device->build_bvh(bvh, progress, refit); + return; + } + + BVHMulti *const bvh_multi = static_cast<BVHMulti *>(bvh); + bvh_multi->sub_bvhs.resize(devices.size()); + + vector<BVHMulti *> geom_bvhs; + geom_bvhs.reserve(bvh->geometry.size()); + foreach (Geometry *geom, bvh->geometry) { + geom_bvhs.push_back(static_cast<BVHMulti *>(geom->bvh)); + } + /* Broadcast acceleration structure build to all render devices */ + size_t i = 0; foreach (SubDevice &sub, devices) { - if (!sub.device->build_optix_bvh(bvh)) - return false; + /* Change geometry BVH pointers to the sub BVH */ + for (size_t k = 0; k < bvh->geometry.size(); ++k) { + bvh->geometry[k]->bvh = geom_bvhs[k]->sub_bvhs[i]; + } + + if (!bvh_multi->sub_bvhs[i]) { + BVHParams params = bvh->params; + if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX) + params.bvh_layout = BVH_LAYOUT_OPTIX; + else if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) + params.bvh_layout = sub.device->info.type == DEVICE_OPTIX ? BVH_LAYOUT_OPTIX : + BVH_LAYOUT_EMBREE; + + /* Skip building a bottom level acceleration structure for non-instanced geometry on Embree + * (since they are put into the top level directly, see bvh_embree.cpp) */ + if (!params.top_level && params.bvh_layout == BVH_LAYOUT_EMBREE && + !bvh->geometry[0]->is_instanced()) { + i++; + continue; + } + + bvh_multi->sub_bvhs[i] = BVH::create(params, bvh->geometry, bvh->objects, sub.device); + } + + sub.device->build_bvh(bvh_multi->sub_bvhs[i], progress, refit); + i++; } - return true; - } - virtual void *bvh_device() const - { - /* CPU devices will always be at the back, so simply choose the last one. - There should only ever be one CPU device anyway and we need the Embree device for it. */ - return devices.back().device->bvh_device(); + /* Change geomtry BVH pointers back to the multi BVH */ + for (size_t k = 0; k < bvh->geometry.size(); ++k) { + bvh->geometry[k]->bvh = geom_bvhs[k]; + } } - virtual void *osl_memory() + virtual void *osl_memory() override { if (devices.size() > 1) { return NULL; @@ -252,7 +307,7 @@ class MultiDevice : public Device { return devices.front().device->osl_memory(); } - bool is_resident(device_ptr key, Device *sub_device) + bool is_resident(device_ptr key, Device *sub_device) override { foreach (SubDevice &sub, devices) { if (sub.device == sub_device) { @@ -299,7 +354,7 @@ class MultiDevice : public Device { return find_matching_mem_device(key, sub)->ptr_map[key]; } - void mem_alloc(device_memory &mem) + void mem_alloc(device_memory &mem) override { device_ptr key = unique_key++; @@ -335,7 +390,7 @@ class MultiDevice : public Device { stats.mem_alloc(mem.device_size); } - void mem_copy_to(device_memory &mem) + void mem_copy_to(device_memory &mem) override { device_ptr existing_key = mem.device_pointer; device_ptr key = (existing_key) ? existing_key : unique_key++; @@ -378,7 +433,7 @@ class MultiDevice : public Device { stats.mem_alloc(mem.device_size - existing_size); } - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) + void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override { device_ptr key = mem.device_pointer; int i = 0, sub_h = h / devices.size(); @@ -399,7 +454,7 @@ class MultiDevice : public Device { mem.device_pointer = key; } - void mem_zero(device_memory &mem) + void mem_zero(device_memory &mem) override { device_ptr existing_key = mem.device_pointer; device_ptr key = (existing_key) ? existing_key : unique_key++; @@ -454,7 +509,7 @@ class MultiDevice : public Device { stats.mem_alloc(mem.device_size - existing_size); } - void mem_free(device_memory &mem) + void mem_free(device_memory &mem) override { device_ptr key = mem.device_pointer; size_t existing_size = mem.device_size; @@ -510,7 +565,7 @@ class MultiDevice : public Device { stats.mem_free(existing_size); } - void const_copy_to(const char *name, void *host, size_t size) + void const_copy_to(const char *name, void *host, size_t size) override { foreach (SubDevice &sub, devices) sub.device->const_copy_to(name, host, size); @@ -527,7 +582,7 @@ class MultiDevice : public Device { int dw, int dh, bool transparent, - const DeviceDrawParams &draw_params) + const DeviceDrawParams &draw_params) override { assert(rgba.type == MEM_PIXELS); @@ -551,7 +606,7 @@ class MultiDevice : public Device { rgba.device_pointer = key; } - void map_tile(Device *sub_device, RenderTile &tile) + void map_tile(Device *sub_device, RenderTile &tile) override { if (!tile.buffer) { return; @@ -572,7 +627,7 @@ class MultiDevice : public Device { } } - int device_number(Device *sub_device) + int device_number(Device *sub_device) override { int i = 0; @@ -591,7 +646,7 @@ class MultiDevice : public Device { return -1; } - void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) + void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override { for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { RenderTile &tile = neighbors.tiles[i]; @@ -643,7 +698,7 @@ class MultiDevice : public Device { } } - void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) + void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override { RenderTile &target_tile = neighbors.target; device_vector<float> &mem = target_tile.buffers->buffer; @@ -677,7 +732,7 @@ class MultiDevice : public Device { } } - int get_split_task_count(DeviceTask &task) + int get_split_task_count(DeviceTask &task) override { int total_tasks = 0; list<DeviceTask> tasks; @@ -693,7 +748,7 @@ class MultiDevice : public Device { return total_tasks; } - void task_add(DeviceTask &task) + void task_add(DeviceTask &task) override { list<SubDevice> task_devices = devices; if (!denoising_devices.empty()) { @@ -743,7 +798,7 @@ class MultiDevice : public Device { } } - void task_wait() + void task_wait() override { foreach (SubDevice &sub, devices) sub.device->task_wait(); @@ -751,7 +806,7 @@ class MultiDevice : public Device { sub.device->task_wait(); } - void task_cancel() + void task_cancel() override { foreach (SubDevice &sub, devices) sub.device->task_cancel(); diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index c6276c1e955..a721f426dfe 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -31,6 +31,7 @@ # include "util/util_logging.h" # include "util/util_md5.h" # include "util/util_path.h" +# include "util/util_progress.h" # include "util/util_time.h" # ifdef WITH_CUDA_DYNLOAD @@ -186,7 +187,6 @@ class OptiXDevice : public CUDADevice { bool motion_blur = false; device_vector<SbtRecord> sbt_data; device_only_memory<KernelParams> launch_params; - vector<CUdeviceptr> as_mem; OptixTraversableHandle tlas_handle = 0; OptixDenoiser denoiser = NULL; @@ -258,11 +258,6 @@ class OptiXDevice : public CUDADevice { // Make CUDA context current const CUDAContextScope scope(cuContext); - // Free all acceleration structures - for (CUdeviceptr mem : as_mem) { - cuMemFree(mem); - } - sbt_data.free(); texture_info.free(); launch_params.free(); @@ -1136,11 +1131,10 @@ class OptiXDevice : public CUDADevice { } } - bool build_optix_bvh(const OptixBuildInput &build_input, - uint16_t num_motion_steps, - OptixTraversableHandle &out_handle, - CUdeviceptr &out_data, - OptixBuildOperation operation) + bool build_optix_bvh(BVHOptiX *bvh, + OptixBuildOperation operation, + const OptixBuildInput &build_input, + uint16_t num_motion_steps) { const CUDAContextScope scope(cuContext); @@ -1166,24 +1160,21 @@ class OptiXDevice : public CUDADevice { optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes)); // Allocate required output buffers - device_only_memory<char> temp_mem(this, "temp_build_mem"); + device_only_memory<char> temp_mem(this, "optix temp as build mem"); temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8); if (!temp_mem.device_pointer) return false; // Make sure temporary memory allocation succeeded - // Move textures to host memory if there is not enough room - size_t size = 0, free = 0; - cuMemGetInfo(&free, &size); - size = sizes.outputSizeInBytes + device_working_headroom; - if (size >= free && can_map_host) { - move_textures_to_host(size - free, false); - } - + device_only_memory<char> &out_data = bvh->as_data; if (operation == OPTIX_BUILD_OPERATION_BUILD) { - check_result_cuda_ret(cuMemAlloc(&out_data, sizes.outputSizeInBytes)); + assert(out_data.device == this); + out_data.alloc_to_device(sizes.outputSizeInBytes); + if (!out_data.device_pointer) + return false; + } + else { + assert(out_data.device_pointer && out_data.device_size >= sizes.outputSizeInBytes); } - - as_mem.push_back(out_data); // Finally build the acceleration structure OptixAccelEmitDesc compacted_size_prop; @@ -1192,6 +1183,7 @@ class OptiXDevice : public CUDADevice { // Make sure this pointer is 8-byte aligned compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8); + OptixTraversableHandle out_handle = 0; check_result_optix_ret(optixAccelBuild(context, NULL, &options, @@ -1199,11 +1191,12 @@ class OptiXDevice : public CUDADevice { 1, temp_mem.device_pointer, sizes.tempSizeInBytes, - out_data, + out_data.device_pointer, sizes.outputSizeInBytes, &out_handle, background ? &compacted_size_prop : NULL, background ? 1 : 0)); + bvh->traversable_handle = static_cast<uint64_t>(out_handle); // Wait for all operations to finish check_result_cuda_ret(cuStreamSynchronize(NULL)); @@ -1219,81 +1212,60 @@ class OptiXDevice : public CUDADevice { // There is no point compacting if the size does not change if (compacted_size < sizes.outputSizeInBytes) { - CUdeviceptr compacted_data = 0; - if (cuMemAlloc(&compacted_data, compacted_size) != CUDA_SUCCESS) + device_only_memory<char> compacted_data(this, "optix compacted as"); + compacted_data.alloc_to_device(compacted_size); + if (!compacted_data.device_pointer) // Do not compact if memory allocation for compacted acceleration structure fails // Can just use the uncompacted one then, so succeed here regardless return true; - as_mem.push_back(compacted_data); - check_result_optix_ret(optixAccelCompact( - context, NULL, out_handle, compacted_data, compacted_size, &out_handle)); + check_result_optix_ret(optixAccelCompact(context, + NULL, + out_handle, + compacted_data.device_pointer, + compacted_size, + &out_handle)); + bvh->traversable_handle = static_cast<uint64_t>(out_handle); // Wait for compaction to finish check_result_cuda_ret(cuStreamSynchronize(NULL)); - // Free uncompacted acceleration structure - cuMemFree(out_data); - as_mem.erase(as_mem.end() - 2); // Remove 'out_data' from 'as_mem' array + std::swap(out_data.device_size, compacted_data.device_size); + std::swap(out_data.device_pointer, compacted_data.device_pointer); } } return true; } - bool build_optix_bvh(BVH *bvh) override + void build_bvh(BVH *bvh, Progress &progress, bool refit) override { - assert(bvh->params.top_level); - - unsigned int num_instances = 0; - unordered_map<Geometry *, OptixTraversableHandle> geometry; - geometry.reserve(bvh->geometry.size()); + BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh); - // Free all previous acceleration structures which can not be refit - std::set<CUdeviceptr> refit_mem; + progress.set_substatus("Building OptiX acceleration structure"); - for (Geometry *geom : bvh->geometry) { - if (static_cast<BVHOptiX *>(geom->bvh)->do_refit) { - refit_mem.insert(static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle); - } - } + if (!bvh->params.top_level) { + assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1); - for (CUdeviceptr mem : as_mem) { - if (refit_mem.find(mem) == refit_mem.end()) { - cuMemFree(mem); - } - } - - as_mem.clear(); - - // Build bottom level acceleration structures (BLAS) - // Note: Always keep this logic in sync with bvh_optix.cpp! - for (Object *ob : bvh->objects) { - // Skip geometry for which acceleration structure already exists - Geometry *geom = ob->get_geometry(); - if (geometry.find(geom) != geometry.end()) - continue; - - OptixTraversableHandle handle; - OptixBuildOperation operation; - CUdeviceptr out_data; - // Refit is only possible in viewport for now. - if (static_cast<BVHOptiX *>(geom->bvh)->do_refit && !background) { - out_data = static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle; - handle = static_cast<BVHOptiX *>(geom->bvh)->optix_handle; + // Refit is only possible in viewport for now (because AS is built with + // OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above) + OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD; + if (refit && !background) { + assert(bvh_optix->traversable_handle != 0); operation = OPTIX_BUILD_OPERATION_UPDATE; } else { - out_data = 0; - handle = 0; - operation = OPTIX_BUILD_OPERATION_BUILD; + bvh_optix->as_data.free(); + bvh_optix->traversable_handle = 0; } + // Build bottom level acceleration structures (BLAS) + Geometry *const geom = bvh->geometry[0]; if (geom->geometry_type == Geometry::HAIR) { // Build BLAS for curve primitives - Hair *const hair = static_cast<Hair *const>(ob->get_geometry()); + Hair *const hair = static_cast<Hair *const>(geom); if (hair->num_curves() == 0) { - continue; + return; } const size_t num_segments = hair->num_segments(); @@ -1304,10 +1276,10 @@ class OptiXDevice : public CUDADevice { num_motion_steps = hair->get_motion_steps(); } - device_vector<OptixAabb> aabb_data(this, "temp_aabb_data", MEM_READ_ONLY); + device_vector<OptixAabb> aabb_data(this, "optix temp aabb data", MEM_READ_ONLY); # if OPTIX_ABI_VERSION >= 36 - device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY); - device_vector<float4> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY); + device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY); + device_vector<float4> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); // Four control points for each curve segment const size_t num_vertices = num_segments * 4; if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { @@ -1325,7 +1297,7 @@ class OptiXDevice : public CUDADevice { size_t center_step = (num_motion_steps - 1) / 2; if (step != center_step) { size_t attr_offset = (step > center_step) ? step - 1 : step; - // Technically this is a float4 array, but sizeof(float3) is the same as sizeof(float4) + // Technically this is a float4 array, but sizeof(float3) == sizeof(float4) keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size(); } @@ -1452,22 +1424,15 @@ class OptiXDevice : public CUDADevice { # endif } - // Allocate memory for new BLAS and build it - if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) { - geometry.insert({ob->get_geometry(), handle}); - static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle = out_data; - static_cast<BVHOptiX *>(geom->bvh)->optix_handle = handle; - static_cast<BVHOptiX *>(geom->bvh)->do_refit = false; - } - else { - return false; + if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { + progress.set_error("Failed to build OptiX acceleration structure"); } } else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { // Build BLAS for triangle primitives - Mesh *const mesh = static_cast<Mesh *const>(ob->get_geometry()); + Mesh *const mesh = static_cast<Mesh *const>(geom); if (mesh->num_triangles() == 0) { - continue; + return; } const size_t num_verts = mesh->get_verts().size(); @@ -1478,12 +1443,12 @@ class OptiXDevice : public CUDADevice { num_motion_steps = mesh->get_motion_steps(); } - device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY); + device_vector<int> index_data(this, "optix temp index data", MEM_READ_ONLY); index_data.alloc(mesh->get_triangles().size()); memcpy(index_data.data(), mesh->get_triangles().data(), mesh->get_triangles().size() * sizeof(int)); - device_vector<float3> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY); + device_vector<float3> vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); vertex_data.alloc(num_verts * num_motion_steps); for (size_t step = 0; step < num_motion_steps; ++step) { @@ -1528,190 +1493,208 @@ class OptiXDevice : public CUDADevice { build_input.triangleArray.numSbtRecords = 1; build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset; - // Allocate memory for new BLAS and build it - if (build_optix_bvh(build_input, num_motion_steps, handle, out_data, operation)) { - geometry.insert({ob->get_geometry(), handle}); - static_cast<BVHOptiX *>(geom->bvh)->optix_data_handle = out_data; - static_cast<BVHOptiX *>(geom->bvh)->optix_handle = handle; - static_cast<BVHOptiX *>(geom->bvh)->do_refit = false; - } - else { - return false; + if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { + progress.set_error("Failed to build OptiX acceleration structure"); } } } + else { + unsigned int num_instances = 0; + + bvh_optix->as_data.free(); + bvh_optix->traversable_handle = 0; + bvh_optix->motion_transform_data.free(); - // Fill instance descriptions + // Fill instance descriptions # if OPTIX_ABI_VERSION < 41 - device_vector<OptixAabb> aabbs(this, "tlas_aabbs", MEM_READ_ONLY); - aabbs.alloc(bvh->objects.size()); + device_vector<OptixAabb> aabbs(this, "optix tlas aabbs", MEM_READ_ONLY); + aabbs.alloc(bvh->objects.size()); # endif - device_vector<OptixInstance> instances(this, "tlas_instances", MEM_READ_ONLY); - instances.alloc(bvh->objects.size()); - - for (Object *ob : bvh->objects) { - // Skip non-traceable objects - if (!ob->is_traceable()) - continue; - - // Create separate instance for triangle/curve meshes of an object - const auto handle_it = geometry.find(ob->get_geometry()); - if (handle_it == geometry.end()) { - continue; + device_vector<OptixInstance> instances(this, "optix tlas instances", MEM_READ_ONLY); + instances.alloc(bvh->objects.size()); + + // Calculate total motion transform size and allocate memory for them + size_t motion_transform_offset = 0; + if (motion_blur) { + size_t total_motion_transform_size = 0; + for (Object *const ob : bvh->objects) { + if (ob->is_traceable() && ob->use_motion()) { + total_motion_transform_size = align_up(total_motion_transform_size, + OPTIX_TRANSFORM_BYTE_ALIGNMENT); + const size_t motion_keys = max(ob->get_motion().size(), 2) - 2; + total_motion_transform_size = total_motion_transform_size + + sizeof(OptixSRTMotionTransform) + + motion_keys * sizeof(OptixSRTData); + } + } + + assert(bvh_optix->motion_transform_data.device == this); + bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size); } - OptixTraversableHandle handle = handle_it->second; + + for (Object *ob : bvh->objects) { + // Skip non-traceable objects + if (!ob->is_traceable()) + continue; + + BVHOptiX *const blas = static_cast<BVHOptiX *>(ob->get_geometry()->bvh); + OptixTraversableHandle handle = blas->traversable_handle; # if OPTIX_ABI_VERSION < 41 - OptixAabb &aabb = aabbs[num_instances]; - aabb.minX = ob->bounds.min.x; - aabb.minY = ob->bounds.min.y; - aabb.minZ = ob->bounds.min.z; - aabb.maxX = ob->bounds.max.x; - aabb.maxY = ob->bounds.max.y; - aabb.maxZ = ob->bounds.max.z; + OptixAabb &aabb = aabbs[num_instances]; + aabb.minX = ob->bounds.min.x; + aabb.minY = ob->bounds.min.y; + aabb.minZ = ob->bounds.min.z; + aabb.maxX = ob->bounds.max.x; + aabb.maxY = ob->bounds.max.y; + aabb.maxZ = ob->bounds.max.z; # endif - OptixInstance &instance = instances[num_instances++]; - memset(&instance, 0, sizeof(instance)); + OptixInstance &instance = instances[num_instances++]; + memset(&instance, 0, sizeof(instance)); - // Clear transform to identity matrix - instance.transform[0] = 1.0f; - instance.transform[5] = 1.0f; - instance.transform[10] = 1.0f; + // Clear transform to identity matrix + instance.transform[0] = 1.0f; + instance.transform[5] = 1.0f; + instance.transform[10] = 1.0f; - // Set user instance ID to object index - instance.instanceId = ob->get_device_index(); + // Set user instance ID to object index + instance.instanceId = ob->get_device_index(); - // Have to have at least one bit in the mask, or else instance would always be culled - instance.visibilityMask = 1; + // Have to have at least one bit in the mask, or else instance would always be culled + instance.visibilityMask = 1; - if (ob->get_geometry()->has_volume) { - // Volumes have a special bit set in the visibility mask so a trace can mask only volumes - instance.visibilityMask |= 2; - } + if (ob->get_geometry()->has_volume) { + // Volumes have a special bit set in the visibility mask so a trace can mask only volumes + instance.visibilityMask |= 2; + } - if (ob->get_geometry()->geometry_type == Geometry::HAIR) { - // Same applies to curves (so they can be skipped in local trace calls) - instance.visibilityMask |= 4; + if (ob->get_geometry()->geometry_type == Geometry::HAIR) { + // Same applies to curves (so they can be skipped in local trace calls) + instance.visibilityMask |= 4; # if OPTIX_ABI_VERSION >= 36 - if (motion_blur && ob->get_geometry()->has_motion_blur() && - DebugFlags().optix.curves_api && - static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) { - // Select between motion blur and non-motion blur built-in intersection module - instance.sbtOffset = PG_HITD_MOTION - PG_HITD; - } + if (motion_blur && ob->get_geometry()->has_motion_blur() && + DebugFlags().optix.curves_api && + static_cast<const Hair *>(ob->get_geometry())->curve_shape == CURVE_THICK) { + // Select between motion blur and non-motion blur built-in intersection module + instance.sbtOffset = PG_HITD_MOTION - PG_HITD; + } # endif - } - - // Insert motion traversable if object has motion - if (motion_blur && ob->use_motion()) { - size_t motion_keys = max(ob->get_motion().size(), 2) - 2; - size_t motion_transform_size = sizeof(OptixSRTMotionTransform) + - motion_keys * sizeof(OptixSRTData); - - const CUDAContextScope scope(cuContext); - - CUdeviceptr motion_transform_gpu = 0; - check_result_cuda_ret(cuMemAlloc(&motion_transform_gpu, motion_transform_size)); - as_mem.push_back(motion_transform_gpu); - - // Allocate host side memory for motion transform and fill it with transform data - OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>( - new uint8_t[motion_transform_size]); - motion_transform.child = handle; - motion_transform.motionOptions.numKeys = ob->get_motion().size(); - motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE; - motion_transform.motionOptions.timeBegin = 0.0f; - motion_transform.motionOptions.timeEnd = 1.0f; - - OptixSRTData *const srt_data = motion_transform.srtData; - array<DecomposedTransform> decomp(ob->get_motion().size()); - transform_motion_decompose( - decomp.data(), ob->get_motion().data(), ob->get_motion().size()); - - for (size_t i = 0; i < ob->get_motion().size(); ++i) { - // Scale - srt_data[i].sx = decomp[i].y.w; // scale.x.x - srt_data[i].sy = decomp[i].z.w; // scale.y.y - srt_data[i].sz = decomp[i].w.w; // scale.z.z - - // Shear - srt_data[i].a = decomp[i].z.x; // scale.x.y - srt_data[i].b = decomp[i].z.y; // scale.x.z - srt_data[i].c = decomp[i].w.x; // scale.y.z - assert(decomp[i].z.z == 0.0f); // scale.y.x - assert(decomp[i].w.y == 0.0f); // scale.z.x - assert(decomp[i].w.z == 0.0f); // scale.z.y - - // Pivot point - srt_data[i].pvx = 0.0f; - srt_data[i].pvy = 0.0f; - srt_data[i].pvz = 0.0f; - - // Rotation - srt_data[i].qx = decomp[i].x.x; - srt_data[i].qy = decomp[i].x.y; - srt_data[i].qz = decomp[i].x.z; - srt_data[i].qw = decomp[i].x.w; - - // Translation - srt_data[i].tx = decomp[i].y.x; - srt_data[i].ty = decomp[i].y.y; - srt_data[i].tz = decomp[i].y.z; } - // Upload motion transform to GPU - cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size); - delete[] reinterpret_cast<uint8_t *>(&motion_transform); + // Insert motion traversable if object has motion + if (motion_blur && ob->use_motion()) { + size_t motion_keys = max(ob->get_motion().size(), 2) - 2; + size_t motion_transform_size = sizeof(OptixSRTMotionTransform) + + motion_keys * sizeof(OptixSRTData); + + const CUDAContextScope scope(cuContext); + + motion_transform_offset = align_up(motion_transform_offset, + OPTIX_TRANSFORM_BYTE_ALIGNMENT); + CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer + + motion_transform_offset; + motion_transform_offset += motion_transform_size; + + // Allocate host side memory for motion transform and fill it with transform data + OptixSRTMotionTransform &motion_transform = *reinterpret_cast<OptixSRTMotionTransform *>( + new uint8_t[motion_transform_size]); + motion_transform.child = handle; + motion_transform.motionOptions.numKeys = ob->get_motion().size(); + motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE; + motion_transform.motionOptions.timeBegin = 0.0f; + motion_transform.motionOptions.timeEnd = 1.0f; + + OptixSRTData *const srt_data = motion_transform.srtData; + array<DecomposedTransform> decomp(ob->get_motion().size()); + transform_motion_decompose( + decomp.data(), ob->get_motion().data(), ob->get_motion().size()); + + for (size_t i = 0; i < ob->get_motion().size(); ++i) { + // Scale + srt_data[i].sx = decomp[i].y.w; // scale.x.x + srt_data[i].sy = decomp[i].z.w; // scale.y.y + srt_data[i].sz = decomp[i].w.w; // scale.z.z + + // Shear + srt_data[i].a = decomp[i].z.x; // scale.x.y + srt_data[i].b = decomp[i].z.y; // scale.x.z + srt_data[i].c = decomp[i].w.x; // scale.y.z + assert(decomp[i].z.z == 0.0f); // scale.y.x + assert(decomp[i].w.y == 0.0f); // scale.z.x + assert(decomp[i].w.z == 0.0f); // scale.z.y + + // Pivot point + srt_data[i].pvx = 0.0f; + srt_data[i].pvy = 0.0f; + srt_data[i].pvz = 0.0f; + + // Rotation + srt_data[i].qx = decomp[i].x.x; + srt_data[i].qy = decomp[i].x.y; + srt_data[i].qz = decomp[i].x.z; + srt_data[i].qw = decomp[i].x.w; + + // Translation + srt_data[i].tx = decomp[i].y.x; + srt_data[i].ty = decomp[i].y.y; + srt_data[i].tz = decomp[i].y.z; + } - // Disable instance transform if object uses motion transform already - instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + // Upload motion transform to GPU + cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size); + delete[] reinterpret_cast<uint8_t *>(&motion_transform); - // Get traversable handle to motion transform - optixConvertPointerToTraversableHandle(context, - motion_transform_gpu, - OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM, - &instance.traversableHandle); - } - else { - instance.traversableHandle = handle; + // Disable instance transform if object uses motion transform already + instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; - if (ob->get_geometry()->is_instanced()) { - // Set transform matrix - memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform)); + // Get traversable handle to motion transform + optixConvertPointerToTraversableHandle(context, + motion_transform_gpu, + OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM, + &instance.traversableHandle); } else { - // Disable instance transform if geometry already has it applied to vertex data - instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; - // Non-instanced objects read ID from prim_object, so - // distinguish them from instanced objects with high bit set - instance.instanceId |= 0x800000; + instance.traversableHandle = handle; + + if (ob->get_geometry()->is_instanced()) { + // Set transform matrix + memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform)); + } + else { + // Disable instance transform if geometry already has it applied to vertex data + instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + // Non-instanced objects read ID from prim_object, so + // distinguish them from instanced objects with high bit set + instance.instanceId |= 0x800000; + } } } - } - // Upload instance descriptions + // Upload instance descriptions # if OPTIX_ABI_VERSION < 41 - aabbs.resize(num_instances); - aabbs.copy_to_device(); + aabbs.resize(num_instances); + aabbs.copy_to_device(); # endif - instances.resize(num_instances); - instances.copy_to_device(); + instances.resize(num_instances); + instances.copy_to_device(); - // Build top-level acceleration structure (TLAS) - OptixBuildInput build_input = {}; - build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES; + // Build top-level acceleration structure (TLAS) + OptixBuildInput build_input = {}; + build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES; # if OPTIX_ABI_VERSION < 41 // Instance AABBs no longer need to be set since OptiX 7.2 - build_input.instanceArray.aabbs = aabbs.device_pointer; - build_input.instanceArray.numAabbs = num_instances; + build_input.instanceArray.aabbs = aabbs.device_pointer; + build_input.instanceArray.numAabbs = num_instances; # endif - build_input.instanceArray.instances = instances.device_pointer; - build_input.instanceArray.numInstances = num_instances; + build_input.instanceArray.instances = instances.device_pointer; + build_input.instanceArray.numInstances = num_instances; - CUdeviceptr out_data = 0; - tlas_handle = 0; - return build_optix_bvh(build_input, 0, tlas_handle, out_data, OPTIX_BUILD_OPERATION_BUILD); + if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) { + progress.set_error("Failed to build OptiX acceleration structure"); + } + tlas_handle = bvh_optix->traversable_handle; + } } void const_copy_to(const char *name, void *host, size_t size) override @@ -1724,7 +1707,7 @@ class OptiXDevice : public CUDADevice { if (strcmp(name, "__data") == 0) { assert(size <= sizeof(KernelData)); - // Fix traversable handle on multi devices + // Update traversable handle (since it is different for each device on multi devices) KernelData *const data = (KernelData *)host; *(OptixTraversableHandle *)&data->bvh.scene = tlas_handle; diff --git a/intern/cycles/device/opencl/opencl_util.cpp b/intern/cycles/device/opencl/opencl_util.cpp index a72fbbad635..be9efcd43ee 100644 --- a/intern/cycles/device/opencl/opencl_util.cpp +++ b/intern/cycles/device/opencl/opencl_util.cpp @@ -780,13 +780,25 @@ bool OpenCLInfo::device_supported(const string &platform_name, const cl_device_i return true; } - /* It is possible to have Iris GPU on AMD/Apple OpenCL framework - * (aka, it will not be on Intel framework). This isn't supported - * and needs an explicit blacklist. - */ - if (strstr(device_name.c_str(), "Iris")) { + /* Allow Intel GPUs on Intel OpenCL platform. */ + if (platform_name.find("Intel") != string::npos) { + if (device_type != CL_DEVICE_TYPE_GPU) { + /* OpenCL on Intel CPU is not an officially supported configuration. + * Use hybrid CPU+GPU rendering to utilize both GPU and CPU. */ + return false; + } + +# ifdef __APPLE__ + /* Apple uses own framework, which can also put Iris onto AMD frame-work. + * This isn't supported configuration. */ return false; +# else + if (device_name.find("Iris") != string::npos || device_name.find("Xe") != string::npos) { + return true; + } +# endif } + if (platform_name == "AMD Accelerated Parallel Processing" && device_type == CL_DEVICE_TYPE_GPU) { if (driver_major < 2236) { diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/bvh_embree.h index ca637288bee..4605c3ea51d 100644 --- a/intern/cycles/kernel/bvh/bvh_embree.h +++ b/intern/cycles/kernel/bvh/bvh_embree.h @@ -112,8 +112,7 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg, RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); isect->prim = hit->primID + - (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) + - kernel_tex_fetch(__object_node, hit->instID[0] / 2); + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); isect->object = hit->instID[0] / 2; } else { @@ -137,8 +136,7 @@ ccl_device_inline void kernel_embree_convert_sss_hit(KernelGlobals *kg, RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( rtcGetGeometry(kernel_data.bvh.scene, local_object_id * 2)); isect->prim = hit->primID + - (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)) + - kernel_tex_fetch(__object_node, local_object_id); + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); isect->object = local_object_id; isect->type = kernel_tex_fetch(__prim_type, isect->prim); } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 6beabebb92f..9d00311f746 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1397,10 +1397,12 @@ typedef enum KernelBVHLayout { BVH_LAYOUT_BVH2 = (1 << 0), BVH_LAYOUT_EMBREE = (1 << 1), BVH_LAYOUT_OPTIX = (1 << 2), + BVH_LAYOUT_MULTI_OPTIX = (1 << 3), + BVH_LAYOUT_MULTI_OPTIX_EMBREE = (1 << 4), /* Default BVH layout to use for CPU. */ BVH_LAYOUT_AUTO = BVH_LAYOUT_EMBREE, - BVH_LAYOUT_ALL = (unsigned int)(~0u), + BVH_LAYOUT_ALL = BVH_LAYOUT_BVH2 | BVH_LAYOUT_EMBREE | BVH_LAYOUT_OPTIX, } KernelBVHLayout; typedef struct KernelBVH { diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 12f4eaf0b79..64b98a91853 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -15,8 +15,7 @@ */ #include "bvh/bvh.h" -#include "bvh/bvh_build.h" -#include "bvh/bvh_embree.h" +#include "bvh/bvh2.h" #include "device/device.h" @@ -41,6 +40,7 @@ #include "util/util_foreach.h" #include "util/util_logging.h" #include "util/util_progress.h" +#include "util/util_task.h" CCL_NAMESPACE_BEGIN @@ -162,7 +162,8 @@ int Geometry::motion_step(float time) const bool Geometry::need_build_bvh(BVHLayout layout) const { - return !transform_applied || has_surface_bssrdf || layout == BVH_LAYOUT_OPTIX; + return is_instanced() || layout == BVH_LAYOUT_OPTIX || layout == BVH_LAYOUT_MULTI_OPTIX || + layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE; } bool Geometry::is_instanced() const @@ -218,7 +219,7 @@ void Geometry::compute_bvh( bvh->geometry = geometry; bvh->objects = objects; - bvh->refit(*progress); + device->build_bvh(bvh, *progress, true); } else { progress->set_status(msg, "Building BVH"); @@ -235,7 +236,7 @@ void Geometry::compute_bvh( delete bvh; bvh = BVH::create(bparams, geometry, objects, device); - MEM_GUARDED_CALL(progress, bvh->build, *progress); + MEM_GUARDED_CALL(progress, device->build_bvh, bvh, *progress, false); } } @@ -1162,25 +1163,66 @@ void GeometryManager::device_update_bvh(Device *device, VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout) << " layout."; - BVH *bvh = BVH::create(bparams, scene->geometry, scene->objects, device); - bvh->build(progress, &device->stats); + delete scene->bvh; + BVH *bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device); + device->build_bvh(bvh, progress, false); if (progress.get_cancel()) { -#ifdef WITH_EMBREE - if (dscene->data.bvh.scene) { - BVHEmbree::destroy(dscene->data.bvh.scene); - dscene->data.bvh.scene = NULL; - } -#endif - delete bvh; return; } + PackedBVH pack; + if (bparams.bvh_layout == BVH_LAYOUT_BVH2) { + pack = std::move(static_cast<BVH2 *>(bvh)->pack); + } + else { + progress.set_status("Updating Scene BVH", "Packing BVH primitives"); + + size_t num_prims = 0; + size_t num_tri_verts = 0; + foreach (Geometry *geom, scene->geometry) { + if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { + Mesh *mesh = static_cast<Mesh *>(geom); + num_prims += mesh->num_triangles(); + num_tri_verts += 3 * mesh->num_triangles(); + } + else if (geom->is_hair()) { + Hair *hair = static_cast<Hair *>(geom); + num_prims += hair->num_segments(); + } + } + + pack.root_index = -1; + pack.prim_tri_index.reserve(num_prims); + pack.prim_tri_verts.reserve(num_tri_verts); + pack.prim_type.reserve(num_prims); + pack.prim_index.reserve(num_prims); + pack.prim_object.reserve(num_prims); + pack.prim_visibility.reserve(num_prims); + + // Merge visibility flags of all objects and find object index for non-instanced geometry + unordered_map<const Geometry *, pair<int, uint>> geometry_to_object_info; + geometry_to_object_info.reserve(scene->geometry.size()); + foreach (Object *ob, scene->objects) { + const Geometry *const geom = ob->get_geometry(); + pair<int, uint> &info = geometry_to_object_info[geom]; + info.second |= ob->visibility_for_tracing(); + if (!geom->is_instanced()) { + info.first = ob->get_device_index(); + } + } + + // Iterate over scene mesh list instead of objects, since 'optix_prim_offset' was calculated + // based on that list, which may be ordered differently from the object list. + foreach (Geometry *geom, scene->geometry) { + const pair<int, uint> &info = geometry_to_object_info[geom]; + geom->pack_primitives(pack, info.first, info.second); + } + } + /* copy to device */ progress.set_status("Updating Scene BVH", "Copying BVH to device"); - PackedBVH &pack = bvh->pack; - if (pack.nodes.size()) { dscene->bvh_nodes.steal_data(pack.nodes); dscene->bvh_nodes.copy_to_device(); @@ -1226,10 +1268,8 @@ void GeometryManager::device_update_bvh(Device *device, dscene->data.bvh.bvh_layout = bparams.bvh_layout; dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0); dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions(); - - bvh->copy_to_device(progress, dscene); - - delete bvh; + /* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */ + dscene->data.bvh.scene = NULL; } void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress) @@ -1653,14 +1693,6 @@ void GeometryManager::device_update(Device *device, void GeometryManager::device_free(Device *device, DeviceScene *dscene) { -#ifdef WITH_EMBREE - if (dscene->data.bvh.scene) { - if (dscene->data.bvh.bvh_layout == BVH_LAYOUT_EMBREE) - BVHEmbree::destroy(dscene->data.bvh.scene); - dscene->data.bvh.scene = NULL; - } -#endif - dscene->bvh_nodes.free(); dscene->bvh_leaf_nodes.free(); dscene->object_node.free(); diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h index 1c101540464..d3daf0cc809 100644 --- a/intern/cycles/render/geometry.h +++ b/intern/cycles/render/geometry.h @@ -41,6 +41,7 @@ class Scene; class SceneParams; class Shader; class Volume; +struct PackedBVH; /* Geometry * @@ -124,6 +125,8 @@ class Geometry : public Node { int n, int total); + virtual void pack_primitives(PackedBVH &pack, int object, uint visibility) = 0; + /* Check whether the geometry should have own BVH built separately. Briefly, * own BVH is needed for geometry, if: * diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp index d67bd209142..896e798b6f9 100644 --- a/intern/cycles/render/hair.cpp +++ b/intern/cycles/render/hair.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ -#include "render/hair.h" +#include "bvh/bvh.h" + #include "render/curves.h" +#include "render/hair.h" #include "render/scene.h" CCL_NAMESPACE_BEGIN @@ -492,4 +494,35 @@ void Hair::pack_curves(Scene *scene, } } +void Hair::pack_primitives(PackedBVH &pack, int object, uint visibility) +{ + if (curve_first_key.empty()) + return; + + const size_t num_prims = num_segments(); + pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims); + pack.prim_type.reserve(pack.prim_type.size() + num_prims); + pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims); + pack.prim_index.reserve(pack.prim_index.size() + num_prims); + pack.prim_object.reserve(pack.prim_object.size() + num_prims); + // 'pack.prim_time' is unused by Embree and OptiX + + uint type = has_motion_blur() ? + ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : + PRIMITIVE_MOTION_CURVE_THICK) : + ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK); + + for (size_t j = 0; j < num_curves(); ++j) { + Curve curve = get_curve(j); + for (size_t k = 0; k < curve.num_segments(); ++k) { + pack.prim_tri_index.push_back_reserved(-1); + pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k)); + pack.prim_visibility.push_back_reserved(visibility); + // Each curve segment points back to its curve index + pack.prim_index.push_back_reserved(j + prim_offset); + pack.prim_object.push_back_reserved(object); + } + } +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h index 32c5b00e879..c7be08d679c 100644 --- a/intern/cycles/render/hair.h +++ b/intern/cycles/render/hair.h @@ -145,6 +145,8 @@ class Hair : public Geometry { /* BVH */ void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset); + + void pack_primitives(PackedBVH &pack, int object, uint visibility) override; }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 3e6ff289c85..30858c4f68b 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -493,8 +493,8 @@ static bool image_associate_alpha(ImageManager::Image *img) template<TypeDesc::BASETYPE FileFormat, typename StorageType> bool ImageManager::file_load_image(Image *img, int texture_limit) { - /* we only handle certain number of components */ - if (!(img->metadata.channels >= 1 && img->metadata.channels <= 4)) { + /* Ignore empty images. */ + if (!(img->metadata.channels > 0)) { return false; } diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 43e664a686f..a0afdd3b841 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -805,4 +805,35 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui } } +void Mesh::pack_primitives(PackedBVH &pack, int object, uint visibility) +{ + if (triangles.empty()) + return; + + const size_t num_prims = num_triangles(); + pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims); + pack.prim_tri_verts.reserve(pack.prim_tri_verts.size() + num_prims * 3); + pack.prim_type.reserve(pack.prim_type.size() + num_prims); + pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims); + pack.prim_index.reserve(pack.prim_index.size() + num_prims); + pack.prim_object.reserve(pack.prim_object.size() + num_prims); + // 'pack.prim_time' is unused by Embree and OptiX + + uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE; + + for (size_t k = 0; k < num_prims; ++k) { + pack.prim_tri_index.push_back_reserved(pack.prim_tri_verts.size()); + + const Mesh::Triangle t = get_triangle(k); + pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[0]])); + pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[1]])); + pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[2]])); + + pack.prim_type.push_back_reserved(type); + pack.prim_visibility.push_back_reserved(visibility); + pack.prim_index.push_back_reserved(k + prim_offset); + pack.prim_object.push_back_reserved(object); + } +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index e2746e560da..b0a16fdfd8f 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -184,9 +184,8 @@ class Mesh : public Geometry { unordered_multimap<int, int> vert_stitching_map; /* stitching index -> multiple real vert indices */ - friend class BVH; + friend class BVH2; friend class BVHBuild; - friend class BVHEmbree; friend class BVHSpatialSplit; friend class DiagSplit; friend class EdgeDice; @@ -233,6 +232,8 @@ class Mesh : public Geometry { size_t tri_offset); void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset); + void pack_primitives(PackedBVH &pack, int object, uint visibility) override; + void tessellate(DiagSplit *split); SubdFace get_subd_face(size_t index) const; diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index 98c256a43b5..b7720b7aa99 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -16,6 +16,7 @@ #include <stdlib.h> +#include "bvh/bvh.h" #include "device/device.h" #include "render/background.h" #include "render/bake.h" @@ -100,6 +101,7 @@ Scene::Scene(const SceneParams ¶ms_, Device *device) { memset((void *)&dscene.data, 0, sizeof(dscene.data)); + bvh = NULL; camera = create_node<Camera>(); dicing_camera = create_node<Camera>(); lookup_tables = new LookupTables(); @@ -135,6 +137,9 @@ Scene::~Scene() void Scene::free_memory(bool final) { + delete bvh; + bvh = NULL; + foreach (Shader *s, shaders) delete s; foreach (Geometry *g, geometry) diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 6686327dc49..27e9a131bbd 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -38,6 +38,7 @@ CCL_NAMESPACE_BEGIN class AttributeRequestSet; class Background; +class BVH; class Camera; class Device; class DeviceInfo; @@ -220,6 +221,7 @@ class Scene : public NodeOwner { string name; /* data */ + BVH *bvh; Camera *camera; Camera *dicing_camera; LookupTables *lookup_tables; diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index cf49dedc426..7e06b427e4d 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -347,8 +347,15 @@ void Shader::tag_update(Scene *scene) foreach (ShaderNode *node, graph->nodes) node->attributes(this, &attributes); - if (has_displacement && displacement_method == DISPLACE_BOTH) { - attributes.add(ATTR_STD_POSITION_UNDISPLACED); + if (has_displacement) { + if (displacement_method == DISPLACE_BOTH) { + attributes.add(ATTR_STD_POSITION_UNDISPLACED); + } + if (displacement_method_is_modified()) { + need_update_geometry = true; + scene->geometry_manager->need_update = true; + scene->object_manager->need_flags_update = true; + } } /* compare if the attributes changed, mesh manager will check diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index e8611839aea..1739659ab88 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -187,6 +187,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) ${X11_X11_INCLUDE_PATH} ) + list(APPEND LIB + ${X11_X11_LIB} + ${X11_Xrender_LIB} + ) + list(APPEND SRC intern/GHOST_DisplayManagerX11.cpp intern/GHOST_SystemX11.cpp @@ -238,6 +243,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) list(APPEND INC_SYS ${X11_xf86vmode_INCLUDE_PATH} ) + list(APPEND LIB + ${X11_Xf86vmode_LIB} + ) endif() if(WITH_X11_XFIXES) @@ -245,6 +253,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) list(APPEND INC_SYS ${X11_Xfixes_INCLUDE_PATH} ) + list(APPEND LIB + ${X11_Xfixes_LIB} + ) endif() if(WITH_X11_ALPHA) @@ -256,6 +267,9 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) list(APPEND INC_SYS ${X11_Xinput_INCLUDE_PATH} ) + list(APPEND LIB + ${X11_Xinput_LIB} + ) endif() add_definitions(-DWITH_GHOST_X11) diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 64740b68c0c..2bc73f7eb22 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1052,7 +1052,7 @@ int GHOST_XrSessionIsRunning(const GHOST_XrContextHandle xr_context); /** * Check if \a xr_context has a session that requires an upside-down frame-buffer (compared to * OpenGL). If true, the render result should be flipped vertically for correct output. - * \note: Only to be called after session start, may otherwise result in a false negative. + * \note Only to be called after session start, may otherwise result in a false negative. */ int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_context); diff --git a/intern/ghost/GHOST_ISystemPaths.h b/intern/ghost/GHOST_ISystemPaths.h index b47d14984d8..e7ac752d322 100644 --- a/intern/ghost/GHOST_ISystemPaths.h +++ b/intern/ghost/GHOST_ISystemPaths.h @@ -78,6 +78,12 @@ class GHOST_ISystemPaths { virtual const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const = 0; /** + * Determine a special ("well known") and easy to reach user directory. + * \return Unsigned char string pointing to user dir (eg `~/Documents/`). + */ + virtual const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const = 0; + + /** * Determine the directory of the current binary * \return Unsigned char string pointing to the binary dir */ diff --git a/intern/ghost/GHOST_Path-api.h b/intern/ghost/GHOST_Path-api.h index 4cc232be6b6..36ea70838ca 100644 --- a/intern/ghost/GHOST_Path-api.h +++ b/intern/ghost/GHOST_Path-api.h @@ -57,6 +57,12 @@ extern const GHOST_TUns8 *GHOST_getSystemDir(int version, const char *versionstr extern const GHOST_TUns8 *GHOST_getUserDir(int version, const char *versionstr); /** + * Determine a special ("well known") and easy to reach user directory. + * \return Unsigned char string pointing to user dir (eg `~/Documents/`). + */ +extern const GHOST_TUns8 *GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type); + +/** * Determine the dir in which the binary file is found. * \return Unsigned char string pointing to binary dir (eg ~/usr/local/bin/). */ diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index a03b59d14b0..8bce064ce63 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -565,6 +565,16 @@ typedef struct { char is_repeat; } GHOST_TEventKeyData; +typedef enum { + GHOST_kUserSpecialDirDesktop, + GHOST_kUserSpecialDirDocuments, + GHOST_kUserSpecialDirDownloads, + GHOST_kUserSpecialDirMusic, + GHOST_kUserSpecialDirPictures, + GHOST_kUserSpecialDirVideos, + /* Can be extended as needed. */ +} GHOST_TUserSpecialDirTypes; + typedef struct { /** Number of pixels on a line. */ GHOST_TUns32 xPixels; diff --git a/intern/ghost/intern/GHOST_Path-api.cpp b/intern/ghost/intern/GHOST_Path-api.cpp index df3592fb5e5..c82e9819f3c 100644 --- a/intern/ghost/intern/GHOST_Path-api.cpp +++ b/intern/ghost/intern/GHOST_Path-api.cpp @@ -50,6 +50,12 @@ const GHOST_TUns8 *GHOST_getUserDir(int version, const char *versionstr) return systemPaths ? systemPaths->getUserDir(version, versionstr) : NULL; /* shouldn't be NULL */ } +const GHOST_TUns8 *GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type) +{ + GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get(); + return systemPaths ? systemPaths->getUserSpecialDir(type) : NULL; /* shouldn't be NULL */ +} + const GHOST_TUns8 *GHOST_getBinaryDir() { GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get(); diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index d5b8311349b..d7dbfbe8813 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -82,8 +82,8 @@ static GHOST_TButtonMask convertButton(int button) /** * Converts Mac rawkey codes (same for Cocoa & Carbon) * into GHOST key codes - * \param rawCode The raw physical key code - * \param recvChar the character ignoring modifiers (except for shift) + * \param rawCode: The raw physical key code + * \param recvChar: the character ignoring modifiers (except for shift) * \return Ghost key code */ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) @@ -783,7 +783,7 @@ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSet /** * Dispose of a context. - * \param context Pointer to the context to be disposed. + * \param context: Pointer to the context to be disposed. * \return Indication of success. */ GHOST_TSuccess GHOST_SystemCocoa::disposeContext(GHOST_IContext *context) @@ -1730,13 +1730,14 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr) } window->clientToScreenIntern(mousePos.x, mousePos.y, x, y); + NSPoint delta = [[cocoawindow contentView] convertPointToBacking:NSMakePoint(dx, dy)]; pushEvent(new GHOST_EventTrackpad([event timestamp] * 1000, window, GHOST_kTrackpadEventScroll, x, y, - dx, - dy, + delta.x, + delta.y, [event isDirectionInvertedFromDevice])); } } break; diff --git a/intern/ghost/intern/GHOST_SystemPathsCocoa.h b/intern/ghost/intern/GHOST_SystemPathsCocoa.h index 188f6f02286..14633d46f03 100644 --- a/intern/ghost/intern/GHOST_SystemPathsCocoa.h +++ b/intern/ghost/intern/GHOST_SystemPathsCocoa.h @@ -56,6 +56,12 @@ class GHOST_SystemPathsCocoa : public GHOST_SystemPaths { const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const; /** + * Determine a special ("well known") and easy to reach user directory. + * \return Unsigned char string pointing to user dir (eg `~/Documents/`). + */ + const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const; + + /** * Determine the directory of the current binary * \return Unsigned char string pointing to the binary dir */ diff --git a/intern/ghost/intern/GHOST_SystemPathsCocoa.mm b/intern/ghost/intern/GHOST_SystemPathsCocoa.mm index 830d58ba42c..7c6184837bf 100644 --- a/intern/ghost/intern/GHOST_SystemPathsCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemPathsCocoa.mm @@ -21,6 +21,7 @@ #import <Foundation/Foundation.h> +#include "GHOST_Debug.h" #include "GHOST_SystemPathsCocoa.h" #pragma mark initialization/finalization @@ -89,6 +90,57 @@ const GHOST_TUns8 *GHOST_SystemPathsCocoa::getUserDir(int, const char *versionst return (GHOST_TUns8 *)tempPath; } +const GHOST_TUns8 *GHOST_SystemPathsCocoa::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const +{ + static char tempPath[512] = ""; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString *basePath; + NSArray *paths; + NSSearchPathDirectory ns_directory; + + switch (type) { + case GHOST_kUserSpecialDirDesktop: + ns_directory = NSDesktopDirectory; + break; + case GHOST_kUserSpecialDirDocuments: + ns_directory = NSDocumentDirectory; + break; + case GHOST_kUserSpecialDirDownloads: + ns_directory = NSDownloadsDirectory; + break; + case GHOST_kUserSpecialDirMusic: + ns_directory = NSMusicDirectory; + break; + case GHOST_kUserSpecialDirPictures: + ns_directory = NSPicturesDirectory; + break; + case GHOST_kUserSpecialDirVideos: + ns_directory = NSMoviesDirectory; + break; + default: + GHOST_ASSERT( + false, + "GHOST_SystemPathsCocoa::getUserSpecialDir(): Invalid enum value for type parameter"); + [pool drain]; + return NULL; + } + + paths = NSSearchPathForDirectoriesInDomains(ns_directory, NSUserDomainMask, YES); + + if ([paths count] > 0) + basePath = [paths objectAtIndex:0]; + else { + [pool drain]; + return NULL; + } + + strncpy( + (char *)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding], sizeof(tempPath)); + + [pool drain]; + return (GHOST_TUns8 *)tempPath; +} + const GHOST_TUns8 *GHOST_SystemPathsCocoa::getBinaryDir() const { static GHOST_TUns8 tempPath[512] = ""; diff --git a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp index ad3d490eb91..86f3a0a31fb 100644 --- a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp +++ b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp @@ -21,6 +21,9 @@ * \ingroup GHOST */ +#include <cstdio> +#include <sstream> + #include "GHOST_SystemPathsUnix.h" #include "GHOST_Debug.h" @@ -108,6 +111,62 @@ const GHOST_TUns8 *GHOST_SystemPathsUnix::getUserDir(int version, const char *ve } } +const GHOST_TUns8 *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const +{ + const char *type_str; + + switch (type) { + case GHOST_kUserSpecialDirDesktop: + type_str = "DESKTOP"; + break; + case GHOST_kUserSpecialDirDocuments: + type_str = "DOCUMENTS"; + break; + case GHOST_kUserSpecialDirDownloads: + type_str = "DOWNLOAD"; + break; + case GHOST_kUserSpecialDirMusic: + type_str = "MUSIC"; + break; + case GHOST_kUserSpecialDirPictures: + type_str = "PICTURES"; + break; + case GHOST_kUserSpecialDirVideos: + type_str = "VIDEOS"; + break; + default: + GHOST_ASSERT( + false, + "GHOST_SystemPathsUnix::getUserSpecialDir(): Invalid enum value for type parameter"); + return NULL; + } + + static string path = ""; + /* Pipe stderr to /dev/null to avoid error prints. We will fail gracefully still. */ + string command = string("xdg-user-dir ") + type_str + " 2> /dev/null"; + + FILE *fstream = popen(command.c_str(), "r"); + if (fstream == NULL) { + return NULL; + } + std::stringstream path_stream; + while (!feof(fstream)) { + char c = fgetc(fstream); + /* xdg-user-dir ends the path with '\n'. */ + if (c == '\n') { + break; + } + path_stream << c; + } + if (pclose(fstream) == -1) { + perror("GHOST_SystemPathsUnix::getUserSpecialDir failed at pclose()"); + return NULL; + } + + path = path_stream.str(); + return path[0] ? (const GHOST_TUns8 *)path.c_str() : NULL; +} + const GHOST_TUns8 *GHOST_SystemPathsUnix::getBinaryDir() const { return NULL; diff --git a/intern/ghost/intern/GHOST_SystemPathsUnix.h b/intern/ghost/intern/GHOST_SystemPathsUnix.h index 8d2f26a28aa..bc9272ecd8f 100644 --- a/intern/ghost/intern/GHOST_SystemPathsUnix.h +++ b/intern/ghost/intern/GHOST_SystemPathsUnix.h @@ -54,6 +54,12 @@ class GHOST_SystemPathsUnix : public GHOST_SystemPaths { const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const; /** + * Determine a special ("well known") and easy to reach user directory. + * \return Unsigned char string pointing to user dir (eg `~/Documents/`). + */ + const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const; + + /** * Determine the directory of the current binary * \return Unsigned char string pointing to the binary dir */ diff --git a/intern/ghost/intern/GHOST_SystemPathsWin32.cpp b/intern/ghost/intern/GHOST_SystemPathsWin32.cpp index 673cbcad97e..193633b5c3e 100644 --- a/intern/ghost/intern/GHOST_SystemPathsWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemPathsWin32.cpp @@ -22,6 +22,7 @@ */ #include "GHOST_SystemPathsWin32.h" +#include "GHOST_Debug.h" #ifndef _WIN32_IE # define _WIN32_IE 0x0501 @@ -76,6 +77,50 @@ const GHOST_TUns8 *GHOST_SystemPathsWin32::getUserDir(int, const char *versionst return NULL; } +const GHOST_TUns8 *GHOST_SystemPathsWin32::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const +{ + GUID folderid; + + switch (type) { + case GHOST_kUserSpecialDirDesktop: + folderid = FOLDERID_Desktop; + break; + case GHOST_kUserSpecialDirDocuments: + folderid = FOLDERID_Documents; + break; + case GHOST_kUserSpecialDirDownloads: + folderid = FOLDERID_Downloads; + break; + case GHOST_kUserSpecialDirMusic: + folderid = FOLDERID_Music; + break; + case GHOST_kUserSpecialDirPictures: + folderid = FOLDERID_Pictures; + break; + case GHOST_kUserSpecialDirVideos: + folderid = FOLDERID_Videos; + break; + default: + GHOST_ASSERT( + false, + "GHOST_SystemPathsWin32::getUserSpecialDir(): Invalid enum value for type parameter"); + return NULL; + } + + static char knownpath[MAX_PATH * 3] = {0}; + PWSTR knownpath_16 = NULL; + HRESULT hResult = SHGetKnownFolderPath(folderid, KF_FLAG_DEFAULT, NULL, &knownpath_16); + + if (hResult == S_OK) { + conv_utf_16_to_8(knownpath_16, knownpath, MAX_PATH * 3); + CoTaskMemFree(knownpath_16); + return (GHOST_TUns8 *)knownpath; + } + + CoTaskMemFree(knownpath_16); + return NULL; +} + const GHOST_TUns8 *GHOST_SystemPathsWin32::getBinaryDir() const { static char fullname[MAX_PATH * 3] = {0}; diff --git a/intern/ghost/intern/GHOST_SystemPathsWin32.h b/intern/ghost/intern/GHOST_SystemPathsWin32.h index 1a1eab21bad..45e03e744a5 100644 --- a/intern/ghost/intern/GHOST_SystemPathsWin32.h +++ b/intern/ghost/intern/GHOST_SystemPathsWin32.h @@ -63,6 +63,12 @@ class GHOST_SystemPathsWin32 : public GHOST_SystemPaths { const GHOST_TUns8 *getUserDir(int version, const char *versionstr) const; /** + * Determine a special ("well known") and easy to reach user directory. + * \return Unsigned char string pointing to user dir (eg `~/Documents/`). + */ + const GHOST_TUns8 *getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const; + + /** * Determine the directory of the current binary * \return Unsigned char string pointing to the binary dir */ diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 8178b9bdf1e..2bf1d0c2d35 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -422,7 +422,7 @@ finished: /** * Dispose of a context. - * \param context Pointer to the context to be disposed. + * \param context: Pointer to the context to be disposed. * \return Indication of success. */ GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context) @@ -1614,7 +1614,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, break; } case WT_PACKET: - window->updatePendingWintabEvents(); + window->updateWintabEventsSyncTime(); break; //////////////////////////////////////////////////////////////////////// // Pointer events, processed diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index fe65162d168..1e1d0814d3a 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -407,7 +407,7 @@ void GHOST_WindowWayland::setOpaque() const #endif /** - * \param type The type of rendering context create. + * \param type: The type of rendering context create. * \return Indication of success. */ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType type) diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index af02663985d..a4cbf66b22d 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -1292,7 +1292,7 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3 GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); - updatePendingWintabEvents(); + updateWintabEvents(); auto &pendingEvents = m_wintab.pendingEvents; size_t pendingEventSize = pendingEvents.size(); @@ -1388,6 +1388,44 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3 return GHOST_kSuccess; } +void GHOST_WindowWin32::updateWintabEvents() +{ + readWintabEvents(); + // When a Wintab device is used to leave window focus, some of it's packets are periodically not + // queued in time to be flushed. Reading packets needs to occur before expiring packets to clear + // these from the queue. + expireWintabEvents(); +} + +void GHOST_WindowWin32::updateWintabEventsSyncTime() +{ + readWintabEvents(); + + if (!m_wintab.pendingEvents.empty()) { + auto lastEvent = m_wintab.pendingEvents.back(); + m_wintab.sysTimeOffset = ::GetTickCount() - lastEvent.pkTime; + } + + expireWintabEvents(); +} + +void GHOST_WindowWin32::readWintabEvents() +{ + if (!(m_wintab.packetsGet && m_wintab.context)) { + return; + } + + auto &pendingEvents = m_wintab.pendingEvents; + + /* Get new packets. */ + const int numPackets = m_wintab.packetsGet( + m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data()); + + for (int i = 0; i < numPackets; i++) { + pendingEvents.push(m_wintab.pkts[i]); + } +} + /* Wintab (per documentation but may vary with implementation) does not update when its event * buffer is full. This is an issue because we need some synchronization point between Wintab * events and Win32 events, so we can't drain and process the queue immediately. We need to @@ -1396,17 +1434,12 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3 * mode from the Wintab API alone. There is no guaranteed ordering between Wintab and Win32 mouse * events and no documented time stamp shared between the two, so we synchronize on mouse button * events. */ -void GHOST_WindowWin32::updatePendingWintabEvents() +void GHOST_WindowWin32::expireWintabEvents() { - if (!(m_wintab.packetsGet && m_wintab.context)) { - return; - } - auto &pendingEvents = m_wintab.pendingEvents; - /* Clear outdated events from queue. */ - DWORD currTime = ::GetTickCount(); - DWORD millisTimeout = 500; + DWORD currTime = ::GetTickCount() - m_wintab.sysTimeOffset; + DWORD millisTimeout = 300; while (!pendingEvents.empty()) { DWORD pkTime = pendingEvents.front().pkTime; @@ -1417,24 +1450,6 @@ void GHOST_WindowWin32::updatePendingWintabEvents() break; } } - - /* Get new packets. */ - const int numPackets = m_wintab.packetsGet( - m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data()); - - int i = 0; - /* Don't queue outdated packets, such events can include packets that occurred before the current - * window lost and regained focus. */ - for (; i < numPackets; i++) { - DWORD pkTime = m_wintab.pkts[i].pkTime; - - if (currTime < pkTime + millisTimeout) { - break; - } - } - for (; i < numPackets; i++) { - pendingEvents.push(m_wintab.pkts[i]); - } } GHOST_TUns16 GHOST_WindowWin32::getDPIHint() diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index a761d7d84dc..829bbdea051 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -486,9 +486,14 @@ class GHOST_WindowWin32 : public GHOST_Window { GHOST_TSuccess getWintabInfo(std::vector<GHOST_WintabInfoWin32> &outWintabInfo); /** - * Updates stored pending Wintab events. + * Updates pending Wintab events and syncs Wintab time with OS time. */ - void updatePendingWintabEvents(); + void updateWintabEventsSyncTime(); + + /** + * Updates pending Wintab events. + */ + void updateWintabEvents(); GHOST_TSuccess beginFullScreen() const { @@ -629,6 +634,7 @@ class GHOST_WindowWin32 : public GHOST_Window { GHOST_TUns8 numSysButtons = 0; /** Cursors currently in contact mapped to system buttons */ DWORD sysButtonsPressed = 0; + DWORD sysTimeOffset = 0; LONG maxPressure = 0; LONG maxAzimuth = 0, maxAltitude = 0; /** Reusable buffer to read in Wintab Packets. */ @@ -642,6 +648,10 @@ class GHOST_WindowWin32 : public GHOST_Window { */ void initializeWintab(); + void readWintabEvents(); + + void expireWintabEvents(); + /** * Convert Wintab system mapped (mouse) buttons into Ghost button mask. * \param cursor: The Wintab cursor associated to the button. diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 9e40c01dffd3f720b23b906d20df8e999d34a4a +Subproject 877a343fed9613d8e02e7fe7181d3bbb628506f diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index 7a521d41b88..b448a230da8 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -55,7 +55,6 @@ const UserDef U_default = { .timecode_style = USER_TIMECODE_MINIMAL, .versions = 1, .dbl_click_time = 350, - .wheellinescroll = 3, .mini_axis_type = USER_MINI_AXIS_TYPE_GIZMO, .uiflag = (USER_FILTERFILEEXTS | USER_DRAWVIEWINFO | USER_PLAINMENUS | USER_LOCK_CURSOR_ADJUST | USER_DEPTH_CURSOR | USER_AUTOPERSP | USER_GLOBALUNDO | diff --git a/release/license/THIRD-PARTY-LICENSES.txt b/release/license/THIRD-PARTY-LICENSES.txt new file mode 100644 index 00000000000..3116635ba29 --- /dev/null +++ b/release/license/THIRD-PARTY-LICENSES.txt @@ -0,0 +1,3973 @@ +** Blosc; version 1.5.0 -- https://github.com/Blosc/c-blosc +For Blosc - A blocking, shuffling and lossless compression library + +Copyright (C) 2009-2018 Francesc Alted <francesc@blosc.org> +Copyright (C) 2019-present Blosc Development team <blosc@blosc.org> + +Copyright (C) 2006 by Rob Landley <rob@landley.net> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +------ + +** Audaspace; version 1.3.0 -- https://audaspace.github.io/ +** Cuda Wrangler; version cbf465b -- https://github.com/CudaWrangler/cuew +** Draco; version 1.3.6 -- https://google.github.io/draco/ +** Embree; version 3.10 -- https://github.com/embree/embree +** Mantaflow; version 0.13 -- http://mantaflow.com/ +** oneAPI Threading Building Block; version 2019_U9 -- +https://software.intel.com/en-us/oneapi/onetbb +** OpenCL Wrangler; version 27a6867 -- https://github.com/OpenCLWrangler/clew +** OpenImageDenoise; version 1.2.3 -- https://www.openimagedenoise.org/ +** OpenXR SDK; version 1.0.8 -- https://khronos.org/openxr +** RangeTree; version 40ebed8aa209 -- https://github.com/ideasman42/rangetree-c +** SDL Extension Wrangler; version 15edf8e -- +https://github.com/SDLWrangler/sdlew + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND +DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, and + distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by the + copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all other + entities that control, are controlled by, or are under common control + with that entity. For the purposes of this definition, "control" means + (i) the power, direct or indirect, to cause the direction or management + of such entity, whether by contract or otherwise, or (ii) ownership of + fifty percent (50%) or more of the outstanding shares, or (iii) + beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity exercising + permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation source, + and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but not limited + to compiled object code, generated documentation, and conversions to + other media types. + + "Work" shall mean the work of authorship, whether in Source or Object + form, made available under the License, as indicated by a copyright + notice that is included in or attached to the work (an example is + provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object form, + that is based on (or derived from) the Work and for which the editorial + revisions, annotations, elaborations, or other modifications represent, + as a whole, an original work of authorship. For the purposes of this + License, Derivative Works shall not include works that remain separable + from, or merely link (or bind by name) to the interfaces of, the Work and + Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including the original + version of the Work and any modifications or additions to that Work or + Derivative Works thereof, that is intentionally submitted to Licensor for + inclusion in the Work by the copyright owner or by an individual or Legal + Entity authorized to submit on behalf of the copyright owner. For the + purposes of this definition, "submitted" means any form of electronic, + verbal, or written communication sent to the Licensor or its + representatives, including but not limited to communication on electronic + mailing lists, source code control systems, and issue tracking systems + that are managed by, or on behalf of, the Licensor for the purpose of + discussing and improving the Work, but excluding communication that is + conspicuously marked or otherwise designated in writing by the copyright + owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity on + behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this + License, each Contributor hereby grants to You a perpetual, worldwide, + non-exclusive, no-charge, royalty-free, irrevocable copyright license to + reproduce, prepare Derivative Works of, publicly display, publicly perform, + sublicense, and distribute the Work and such Derivative Works in Source or + Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this + License, each Contributor hereby grants to You a perpetual, worldwide, + non-exclusive, no-charge, royalty-free, irrevocable (except as stated in + this section) patent license to make, have made, use, offer to sell, sell, + import, and otherwise transfer the Work, where such license applies only to + those patent claims licensable by such Contributor that are necessarily + infringed by their Contribution(s) alone or by combination of their + Contribution(s) with the Work to which such Contribution(s) was submitted. + If You institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work or a + Contribution incorporated within the Work constitutes direct or contributory + patent infringement, then any patent licenses granted to You under this + License for that Work shall terminate as of the date such litigation is + filed. + + 4. Redistribution. You may reproduce and distribute copies of the Work or + Derivative Works thereof in any medium, with or without modifications, and + in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a + copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating + that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices + from the Source form of the Work, excluding those notices that do not + pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must include + a readable copy of the attribution notices contained within such NOTICE + file, excluding those notices that do not pertain to any part of the + Derivative Works, in at least one of the following places: within a + NOTICE text file distributed as part of the Derivative Works; within the + Source form or documentation, if provided along with the Derivative + Works; or, within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents of the + NOTICE file are for informational purposes only and do not modify the + License. You may add Your own attribution notices within Derivative Works + that You distribute, alongside or as an addendum to the NOTICE text from + the Work, provided that such additional attribution notices cannot be + construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may + provide additional or different license terms and conditions for use, + reproduction, or distribution of Your modifications, or for any such + Derivative Works as a whole, provided Your use, reproduction, and + distribution of the Work otherwise complies with the conditions stated in + this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any + Contribution intentionally submitted for inclusion in the Work by You to the + Licensor shall be under the terms and conditions of this License, without + any additional terms or conditions. Notwithstanding the above, nothing + herein shall supersede or modify the terms of any separate license agreement + you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, except + as required for reasonable and customary use in describing the origin of the + Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in + writing, Licensor provides the Work (and each Contributor provides its + Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied, including, without limitation, any + warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or + FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining + the appropriateness of using or redistributing the Work and assume any risks + associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether + in tort (including negligence), contract, or otherwise, unless required by + applicable law (such as deliberate and grossly negligent acts) or agreed to + in writing, shall any Contributor be liable to You for damages, including + any direct, indirect, special, incidental, or consequential damages of any + character arising as a result of this License or out of the use or inability + to use the Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all other + commercial damages or losses), even if such Contributor has been advised of + the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work + or Derivative Works thereof, You may choose to offer, and charge a fee for, + acceptance of support, warranty, indemnity, or other liability obligations + and/or rights consistent with this License. However, in accepting such + obligations, You may act only on Your own behalf and on Your sole + responsibility, not on behalf of any other Contributor, and only if You + agree to indemnify, defend, and hold each Contributor harmless for any + liability incurred by, or claims asserted against, such Contributor by + reason of your accepting any such warranty or additional liability. END OF + TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification +within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + +* For Audaspace see also this required NOTICE: + Copyright © 2009-2020 Jörg Müller. All rights reserved. +* For Cuda Wrangler see also this required NOTICE: + Copyright 2011-2014 Blender Foundation +* For Draco see also this required NOTICE: + Copyright 2018 The Draco Authors +* For Embree see also this required NOTICE: + Copyright 2009-2020 Intel Corporation +* For Mantaflow see also this required NOTICE: + MantaFlow fluid solver framework + Copyright 2011 Tobias Pfaff, Nils Thuerey +* For oneAPI Threading Building Block see also this required NOTICE: + Copyright (c) 2005-2020 Intel Corporation +* For OpenCL Wrangler see also this required NOTICE: + Copyright (c) 2009 Organic Vectory B.V. + Written by George van Venrooij +* For OpenImageDenoise see also this required NOTICE: + Copyright 2009-2020 Intel Corporation +* For OpenXR SDK see also this required NOTICE: + Copyright (c) 2017-2020 The Khronos Group Inc. + Copyright (c) 2017-2019 Valve Corporation + Copyright (c) 2017-2019 LunarG, Inc. + Copyright (c) 2019 Collabora, Ltd. +* For RangeTree see also this required NOTICE: + Copyright (c) 2016, Campbell Barton. +* For SDL Extension Wrangler see also this required NOTICE: + Copyright 2014 Blender Foundation + +------ + +** OpenJPEG; version 2.3.1 -- https://github.com/uclouvain/openjpeg +Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium +Copyright (c) 2002-2014, Professor Benoit Macq +Copyright (c) 2003-2014, Antonin Descampe +Copyright (c) 2003-2009, Francois-Olivier Devaux +Copyright (c) 2005, Herve Drolon, FreeImage Team +Copyright (c) 2002-2003, Yannick Verschueren +Copyright (c) 2001-2003, David Janssens +Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France +Copyright (c) 2012, CS Systemes d'Information, France + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------ + +** Ceres Solver; version 2.0.0 -- http://ceres-solver.org/ +Ceres Solver - A fast non-linear least squares minimizer +Copyright 2016 Google Inc. All rights reserved. +** Curve-Fit-nD; version ddcd5bd -- https://github.com/ideasman42/curve-fit-nd +Copyright (c) 2016, Blender Foundation. +** Google C++ Testing Framework; version 1.10.0 -- +https://github.com/google/googletest +Copyright 2007, Google Inc. +All rights reserved. +** Google Flags; version 2.2.1 -- https://github.com/gflags/gflags +Copyright (c) 1999, Google Inc. +All rights reserved. +** Google Logging; version 4.4.0 -- https://github.com/google/glog +Copyright (c) 2006, Google Inc. +All rights reserved. +** Open Shading Language; version 1.10.10 -- +https://github.com/imageworks/OpenShadingLanguage +Copyright Contributors to the Open Shading Language project. +** OpenColorIO; version 1.1.1 -- +https://github.com/AcademySoftwareFoundation/OpenColorIO +Copyright Contributors to the OpenColorIO Project. +** OpenEXR; version 2.4.0 -- +https://github.com/AcademySoftwareFoundation/openexr +Copyright Contributors to the OpenEXR Project. All rights reserved. +** OpenImageIO; version 2.1.15.0 -- http://www.openimageio.org +Copyright (c) 2008-present by Contributors to the OpenImageIO project. All +Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------ + +** AutoPackage; version 1.0 -- http://autopackage.org +BinReloc - a library for creating relocatable executables +Written by: Hongli Lai <h.lai@chello.nl> +** LZMA SDK; version 4.65 -- https://www.7-zip.org/sdk.html +LZMA SDK: Public Domain + +Creative Commons Legal Code + +CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT +PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN +ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN +"AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS +DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS +LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE +INFORMATION OR WORKS PROVIDED HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific works +("Commons") that the public can reliably and without fear of later claims of +infringement build upon, modify, incorporate in other works, reuse and +redistribute as freely as possible in any form whatsoever and for any purposes, +including without limitation commercial purposes. These owners may contribute +to the Commons to promote the ideal of a free culture and the further +production of creative, cultural and scientific works, or to gain reputation or +greater distribution for their Work in part through the use and efforts of +others. + +For these and/or other purposes and motivations, and without any expectation of +additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and +publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + + 1. Copyright and Related Rights. A Work made available under CC0 may be + protected by copyright and related or neighboring rights ("Copyright and + Related Rights"). Copyright and Related Rights include, but are not limited + to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national implementations + thereof. + + 2. Waiver. To the greatest extent permitted by, but not in contravention of, + applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and + unconditionally waives, abandons, and surrenders all of Affirmer's Copyright + and Related Rights and associated claims and causes of action, whether now + known or unknown (including existing as well as future claims and causes of + action), in the Work (i) in all territories worldwide, (ii) for the maximum + duration provided by applicable law or treaty (including future time + extensions), (iii) in any current or future medium and for any number of + copies, and (iv) for any purpose whatsoever, including without limitation + commercial, advertising or promotional purposes (the "Waiver"). Affirmer + makes the Waiver for the benefit of each member of the public at large and + to the detriment of Affirmer's heirs and successors, fully intending that + such Waiver shall not be subject to revocation, rescission, cancellation, + termination, or any other legal or equitable action to disrupt the quiet + enjoyment of the Work by the public as contemplated by Affirmer's express + Statement of Purpose. + + 3. Public License Fallback. Should any part of the Waiver for any reason be + judged legally invalid or ineffective under applicable law, then the Waiver + shall be preserved to the maximum extent permitted taking into account + Affirmer's express Statement of Purpose. In addition, to the extent the + Waiver is so judged Affirmer hereby grants to each affected person a + royalty-free, non transferable, non sublicensable, non exclusive, + irrevocable and unconditional license to exercise Affirmer's Copyright and + Related Rights in the Work (i) in all territories worldwide, (ii) for the + maximum duration provided by applicable law or treaty (including future time + extensions), (iii) in any current or future medium and for any number of + copies, and (iv) for any purpose whatsoever, including without limitation + commercial, advertising or promotional purposes (the "License"). The License + shall be deemed effective as of the date CC0 was applied by Affirmer to the + Work. Should any part of the License for any reason be judged legally + invalid or ineffective under applicable law, such partial invalidity or + ineffectiveness shall not invalidate the remainder of the License, and in + such case Affirmer hereby affirms that he or she will not (i) exercise any + of his or her remaining Copyright and Related Rights in the Work or (ii) + assert any associated claims and causes of action with respect to the Work, + in either case contrary to Affirmer's express Statement of Purpose. + + 4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, statutory + or otherwise, including without limitation warranties of title, + merchantability, fitness for a particular purpose, non infringement, or + the absence of latent or other defects, accuracy, or the present or + absence of errors, whether or not discoverable, all to the greatest + extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +------ + +** Potrace; version 1.16 -- http://potrace.sourceforge.net/ +Copyright (C) 2001-2019 Peter Selinger. + +GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 , USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to most +of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must make +sure that they, too, receive or can get the source code. And you must show them +these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish +to avoid the danger that redistributors of a free program will individually +obtain patent licenses, in effect making the program proprietary. To prevent +this, we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains a notice + placed by the copyright holder saying it may be distributed under the terms + of this General Public License. The "Program", below, refers to any such + program or work, and a "work based on the Program" means either the Program + or any derivative work under copyright law: that is to say, a work + containing the Program or a portion of it, either verbatim or with + modifications and/or translated into another language. (Hereinafter, + translation is included without limitation in the term "modification".) Each + licensee is addressed as "you". + + Activities other than copying, distribution and modification are not covered + by this License; they are outside its scope. The act of running the Program + is not restricted, and the output from the Program is covered only if its + contents constitute a work based on the Program (independent of having been + made by running the Program). Whether that is true depends on what the + Program does. + + 1. You may copy and distribute verbatim copies of the Program's source code + as you receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice and + disclaimer of warranty; keep intact all the notices that refer to this + License and to the absence of any warranty; and give any other recipients of + the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and you + may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion of it, + thus forming a work based on the Program, and copy and distribute such + modifications or work under the terms of Section 1 above, provided that you + also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole + or in part contains or is derived from the Program or any part thereof, + to be licensed as a whole at no charge to all third parties under the + terms of this License. + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use in + the most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a + copy of this License. (Exception: if the Program itself is interactive + but does not normally print such an announcement, your work based on the + Program is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If identifiable + sections of that work are not derived from the Program, and can be + reasonably considered independent and separate works in themselves, then + this License, and its terms, do not apply to those sections when you + distribute them as separate works. But when you distribute the same sections + as part of a whole which is a work based on the Program, the distribution of + the whole must be on the terms of this License, whose permissions for other + licensees extend to the entire whole, and thus to each and every part + regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your + rights to work written entirely by you; rather, the intent is to exercise + the right to control the distribution of derivative or collective works + based on the Program. + + In addition, mere aggregation of another work not based on the Program with + the Program (or with a work based on the Program) on a volume of a storage + or distribution medium does not bring the other work under the scope of this + License. + + 3. You may copy and distribute the Program (or a work based on it, under + Section 2) in object code or executable form under the terms of Sections 1 + and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + + The source code for a work means the preferred form of the work for making + modifications to it. For an executable work, complete source code means all + the source code for all modules it contains, plus any associated interface + definition files, plus the scripts used to control compilation and + installation of the executable. However, as a special exception, the source + code distributed need not include anything that is normally distributed (in + either source or binary form) with the major components (compiler, kernel, + and so on) of the operating system on which the executable runs, unless that + component itself accompanies the executable. + + If distribution of executable or object code is made by offering access to + copy from a designated place, then offering equivalent access to copy the + source code from the same place counts as distribution of the source code, + even though third parties are not compelled to copy the source along with + the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program except as + expressly provided under this License. Any attempt otherwise to copy, + modify, sublicense or distribute the Program is void, and will automatically + terminate your rights under this License. However, parties who have received + copies, or rights, from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + + 5. You are not required to accept this License, since you have not signed + it. However, nothing else grants you permission to modify or distribute the + Program or its derivative works. These actions are prohibited by law if you + do not accept this License. Therefore, by modifying or distributing the + Program (or any work based on the Program), you indicate your acceptance of + this License to do so, and all its terms and conditions for copying, + distributing or modifying the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the original + licensor to copy, distribute or modify the Program subject to these terms + and conditions. You may not impose any further restrictions on the + recipients' exercise of the rights granted herein. You are not responsible + for enforcing compliance by third parties to this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute so + as to satisfy simultaneously your obligations under this License and any + other pertinent obligations, then as a consequence you may not distribute + the Program at all. For example, if a patent license would not permit + royalty-free redistribution of the Program by all those who receive copies + directly or indirectly through you, then the only way you could satisfy both + it and this License would be to refrain entirely from distribution of the + Program. + + If any portion of this section is held invalid or unenforceable under any + particular circumstance, the balance of the section is intended to apply and + the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any patents + or other property right claims or to contest validity of any such claims; + this section has the sole purpose of protecting the integrity of the free + software distribution system, which is implemented by public license + practices. Many people have made generous contributions to the wide range of + software distributed through that system in reliance on consistent + application of that system; it is up to the author/donor to decide if he or + she is willing to distribute software through any other system and a + licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be a + consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in certain + countries either by patents or by copyrighted interfaces, the original + copyright holder who places the Program under this License may add an + explicit geographical distribution limitation excluding those countries, so + that distribution is permitted only in or among countries not thus excluded. + In such case, this License incorporates the limitation as if written in the + body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions of + the General Public License from time to time. Such new versions will be + similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and conditions + either of that version or of any later version published by the Free + Software Foundation. If the Program does not specify a version number of + this License, you may choose any version ever published by the Free Software + Foundation. + + 10. If you wish to incorporate parts of the Program into other free programs + whose distribution conditions are different, write to the author to ask for + permission. For software which is copyrighted by the Free Software + Foundation, write to the Free Software Foundation; we sometimes make + exceptions for this. Our decision will be guided by the two goals of + preserving the free status of all derivatives of our free software and of + promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR + THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO + THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM + PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR + CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, + INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING + OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO + LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR + THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + +<one line to give the program's name and an idea of what it does.> + +Copyright (C) <yyyy> <name of author> + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +Street, Fifth Floor, Boston, MA 02110-1301 , USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + +Gnomovision version 69, Copyright (C) year name of author Gnomovision comes +with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, +and you are welcome to redistribute it under certain conditions; type `show c' +for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here is +a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +< signature of Ty Coon > , 1 April 1989 Ty Coon, President of Vice + +------ + +** FFTW; version 3.3.8 -- http://www.fftw.org/ +Copyright (c) 2003, 2007-14 Matteo Frigo +Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology +** GMP; version 6.2.0 -- https://gmplib.org/ +Copyright 1996-2020 Free Software Foundation, Inc. +** OpenAL; version 1.20.1 -- http://openal-soft.org +Copyright (c) 2015, Archontis Politis +Copyright (c) 2019, Christopher Robinson + +GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 , USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to most +of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must make +sure that they, too, receive or can get the source code. And you must show them +these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish +to avoid the danger that redistributors of a free program will individually +obtain patent licenses, in effect making the program proprietary. To prevent +this, we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains a notice + placed by the copyright holder saying it may be distributed under the terms + of this General Public License. The "Program", below, refers to any such + program or work, and a "work based on the Program" means either the Program + or any derivative work under copyright law: that is to say, a work + containing the Program or a portion of it, either verbatim or with + modifications and/or translated into another language. (Hereinafter, + translation is included without limitation in the term "modification".) Each + licensee is addressed as "you". + + Activities other than copying, distribution and modification are not covered + by this License; they are outside its scope. The act of running the Program + is not restricted, and the output from the Program is covered only if its + contents constitute a work based on the Program (independent of having been + made by running the Program). Whether that is true depends on what the + Program does. + + 1. You may copy and distribute verbatim copies of the Program's source code + as you receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice and + disclaimer of warranty; keep intact all the notices that refer to this + License and to the absence of any warranty; and give any other recipients of + the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and you + may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion of it, + thus forming a work based on the Program, and copy and distribute such + modifications or work under the terms of Section 1 above, provided that you + also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole + or in part contains or is derived from the Program or any part thereof, + to be licensed as a whole at no charge to all third parties under the + terms of this License. + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use in + the most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a + copy of this License. (Exception: if the Program itself is interactive + but does not normally print such an announcement, your work based on the + Program is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If identifiable + sections of that work are not derived from the Program, and can be + reasonably considered independent and separate works in themselves, then + this License, and its terms, do not apply to those sections when you + distribute them as separate works. But when you distribute the same sections + as part of a whole which is a work based on the Program, the distribution of + the whole must be on the terms of this License, whose permissions for other + licensees extend to the entire whole, and thus to each and every part + regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your + rights to work written entirely by you; rather, the intent is to exercise + the right to control the distribution of derivative or collective works + based on the Program. + + In addition, mere aggregation of another work not based on the Program with + the Program (or with a work based on the Program) on a volume of a storage + or distribution medium does not bring the other work under the scope of this + License. + + 3. You may copy and distribute the Program (or a work based on it, under + Section 2) in object code or executable form under the terms of Sections 1 + and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + + The source code for a work means the preferred form of the work for making + modifications to it. For an executable work, complete source code means all + the source code for all modules it contains, plus any associated interface + definition files, plus the scripts used to control compilation and + installation of the executable. However, as a special exception, the source + code distributed need not include anything that is normally distributed (in + either source or binary form) with the major components (compiler, kernel, + and so on) of the operating system on which the executable runs, unless that + component itself accompanies the executable. + + If distribution of executable or object code is made by offering access to + copy from a designated place, then offering equivalent access to copy the + source code from the same place counts as distribution of the source code, + even though third parties are not compelled to copy the source along with + the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program except as + expressly provided under this License. Any attempt otherwise to copy, + modify, sublicense or distribute the Program is void, and will automatically + terminate your rights under this License. However, parties who have received + copies, or rights, from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + + 5. You are not required to accept this License, since you have not signed + it. However, nothing else grants you permission to modify or distribute the + Program or its derivative works. These actions are prohibited by law if you + do not accept this License. Therefore, by modifying or distributing the + Program (or any work based on the Program), you indicate your acceptance of + this License to do so, and all its terms and conditions for copying, + distributing or modifying the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the original + licensor to copy, distribute or modify the Program subject to these terms + and conditions. You may not impose any further restrictions on the + recipients' exercise of the rights granted herein. You are not responsible + for enforcing compliance by third parties to this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute so + as to satisfy simultaneously your obligations under this License and any + other pertinent obligations, then as a consequence you may not distribute + the Program at all. For example, if a patent license would not permit + royalty-free redistribution of the Program by all those who receive copies + directly or indirectly through you, then the only way you could satisfy both + it and this License would be to refrain entirely from distribution of the + Program. + + If any portion of this section is held invalid or unenforceable under any + particular circumstance, the balance of the section is intended to apply and + the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any patents + or other property right claims or to contest validity of any such claims; + this section has the sole purpose of protecting the integrity of the free + software distribution system, which is implemented by public license + practices. Many people have made generous contributions to the wide range of + software distributed through that system in reliance on consistent + application of that system; it is up to the author/donor to decide if he or + she is willing to distribute software through any other system and a + licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be a + consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in certain + countries either by patents or by copyrighted interfaces, the original + copyright holder who places the Program under this License may add an + explicit geographical distribution limitation excluding those countries, so + that distribution is permitted only in or among countries not thus excluded. + In such case, this License incorporates the limitation as if written in the + body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions of + the General Public License from time to time. Such new versions will be + similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and conditions + either of that version or of any later version published by the Free + Software Foundation. If the Program does not specify a version number of + this License, you may choose any version ever published by the Free Software + Foundation. + + 10. If you wish to incorporate parts of the Program into other free programs + whose distribution conditions are different, write to the author to ask for + permission. For software which is copyrighted by the Free Software + Foundation, write to the Free Software Foundation; we sometimes make + exceptions for this. Our decision will be guided by the two goals of + preserving the free status of all derivatives of our free software and of + promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR + THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO + THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM + PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR + CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, + INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING + OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO + LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR + THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + +< one line to give the program's name and an idea of what it does. > + +Copyright (C) < yyyy > < name of author > + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +Street, Fifth Floor, Boston, MA 02110-1301 , USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + +Gnomovision version 69, Copyright (C) year name of author Gnomovision comes +with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, +and you are welcome to redistribute it under certain conditions; type `show c' +for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here is +a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +< signature of Ty Coon > , 1 April 1989 Ty Coon, President of Vice This General +Public License does not permit incorporating your program into proprietary +programs. If your program is a subroutine library, you may consider it more +useful to permit linking proprietary applications with the library. If this is +what you want to do, use the GNU Lesser General Public License instead of this +License. + +------ + +** miniLZO; version 2.08 -- http://www.oberhumer.com/opensource/lzo/ +LZO and miniLZO are Copyright (C) 1996-2014 Markus Franz Xaver Oberhumer +All Rights Reserved. +** The FreeType Project; version 2.10.2 -- +https://sourceforge.net/projects/freetype +Copyright (C) 1996-2020 by David Turner, Robert Wilhelm, and Werner Lemberg. +** X Drag and Drop; version 2000-08-08 -- +https://freedesktop.org/wiki/Specifications/XDND/ +xdnd.c, xdnd.h - C program library for handling the Xdnd protocol +Copyright (C) 1996-2000 Paul Sheer + +GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 , USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to most +of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must make +sure that they, too, receive or can get the source code. And you must show them +these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish +to avoid the danger that redistributors of a free program will individually +obtain patent licenses, in effect making the program proprietary. To prevent +this, we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains a notice + placed by the copyright holder saying it may be distributed under the terms + of this General Public License. The "Program", below, refers to any such + program or work, and a "work based on the Program" means either the Program + or any derivative work under copyright law: that is to say, a work + containing the Program or a portion of it, either verbatim or with + modifications and/or translated into another language. (Hereinafter, + translation is included without limitation in the term "modification".) Each + licensee is addressed as "you". + + Activities other than copying, distribution and modification are not covered + by this License; they are outside its scope. The act of running the Program + is not restricted, and the output from the Program is covered only if its + contents constitute a work based on the Program (independent of having been + made by running the Program). Whether that is true depends on what the + Program does. + + 1. You may copy and distribute verbatim copies of the Program's source code + as you receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice and + disclaimer of warranty; keep intact all the notices that refer to this + License and to the absence of any warranty; and give any other recipients of + the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and you + may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion of it, + thus forming a work based on the Program, and copy and distribute such + modifications or work under the terms of Section 1 above, provided that you + also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole + or in part contains or is derived from the Program or any part thereof, + to be licensed as a whole at no charge to all third parties under the + terms of this License. + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use in + the most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a + copy of this License. (Exception: if the Program itself is interactive + but does not normally print such an announcement, your work based on the + Program is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If identifiable + sections of that work are not derived from the Program, and can be + reasonably considered independent and separate works in themselves, then + this License, and its terms, do not apply to those sections when you + distribute them as separate works. But when you distribute the same sections + as part of a whole which is a work based on the Program, the distribution of + the whole must be on the terms of this License, whose permissions for other + licensees extend to the entire whole, and thus to each and every part + regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your + rights to work written entirely by you; rather, the intent is to exercise + the right to control the distribution of derivative or collective works + based on the Program. + + In addition, mere aggregation of another work not based on the Program with + the Program (or with a work based on the Program) on a volume of a storage + or distribution medium does not bring the other work under the scope of this + License. + + 3. You may copy and distribute the Program (or a work based on it, under + Section 2) in object code or executable form under the terms of Sections 1 + and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + + The source code for a work means the preferred form of the work for making + modifications to it. For an executable work, complete source code means all + the source code for all modules it contains, plus any associated interface + definition files, plus the scripts used to control compilation and + installation of the executable. However, as a special exception, the source + code distributed need not include anything that is normally distributed (in + either source or binary form) with the major components (compiler, kernel, + and so on) of the operating system on which the executable runs, unless that + component itself accompanies the executable. + + If distribution of executable or object code is made by offering access to + copy from a designated place, then offering equivalent access to copy the + source code from the same place counts as distribution of the source code, + even though third parties are not compelled to copy the source along with + the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program except as + expressly provided under this License. Any attempt otherwise to copy, + modify, sublicense or distribute the Program is void, and will automatically + terminate your rights under this License. However, parties who have received + copies, or rights, from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + + 5. You are not required to accept this License, since you have not signed + it. However, nothing else grants you permission to modify or distribute the + Program or its derivative works. These actions are prohibited by law if you + do not accept this License. Therefore, by modifying or distributing the + Program (or any work based on the Program), you indicate your acceptance of + this License to do so, and all its terms and conditions for copying, + distributing or modifying the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the original + licensor to copy, distribute or modify the Program subject to these terms + and conditions. You may not impose any further restrictions on the + recipients' exercise of the rights granted herein. You are not responsible + for enforcing compliance by third parties to this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute so + as to satisfy simultaneously your obligations under this License and any + other pertinent obligations, then as a consequence you may not distribute + the Program at all. For example, if a patent license would not permit + royalty-free redistribution of the Program by all those who receive copies + directly or indirectly through you, then the only way you could satisfy both + it and this License would be to refrain entirely from distribution of the + Program. + + If any portion of this section is held invalid or unenforceable under any + particular circumstance, the balance of the section is intended to apply and + the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any patents + or other property right claims or to contest validity of any such claims; + this section has the sole purpose of protecting the integrity of the free + software distribution system, which is implemented by public license + practices. Many people have made generous contributions to the wide range of + software distributed through that system in reliance on consistent + application of that system; it is up to the author/donor to decide if he or + she is willing to distribute software through any other system and a + licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be a + consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in certain + countries either by patents or by copyrighted interfaces, the original + copyright holder who places the Program under this License may add an + explicit geographical distribution limitation excluding those countries, so + that distribution is permitted only in or among countries not thus excluded. + In such case, this License incorporates the limitation as if written in the + body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions of + the General Public License from time to time. Such new versions will be + similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and conditions + either of that version or of any later version published by the Free + Software Foundation. If the Program does not specify a version number of + this License, you may choose any version ever published by the Free Software + Foundation. + + 10. If you wish to incorporate parts of the Program into other free programs + whose distribution conditions are different, write to the author to ask for + permission. For software which is copyrighted by the Free Software + Foundation, write to the Free Software Foundation; we sometimes make + exceptions for this. Our decision will be guided by the two goals of + preserving the free status of all derivatives of our free software and of + promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR + THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO + THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM + PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR + CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, + INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING + OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO + LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR + THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + +<one line to give the program's name and an idea of what it does.> + +Copyright (C) <yyyy> <name of author> + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +Street, Fifth Floor, Boston, MA 02110-1301 , USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + +Gnomovision version 69, Copyright (C) year name of author Gnomovision comes +with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, +and you are welcome to redistribute it under certain conditions; type `show c' +for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here is +a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +< signature of Ty Coon > , 1 April 1989 Ty Coon, President of Vice This General +Public License does not permit incorporating your program into proprietary +programs. If your program is a subroutine library, you may consider it more +useful to permit linking proprietary applications with the library. If this is +what you want to do, use the GNU Lesser General Public License instead of this +License. + +------ + +** Eigen, template library for linear algebra: matrices, vectors, numerical +solvers, and related algorithms; version 3.2.7 -- +http://eigen.tuxfamily.org/index.php?title=Main_Page +This file is part of Eigen, a lightweight C++ template library for linear +algebra. +** Free Spacenav; version 0.2.3 -- +http://downloads.sourceforge.net/project/spacenav +Copyright (C) 2007-2019 John Tsiombikas <nuclear@member.fsf.org> + +GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. <http s ://fsf.org/> + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for software and +other kinds of works. + +The licenses for most software and other practical works are designed to take +away your freedom to share and change the works. By contrast, the GNU General +Public License is intended to guarantee your freedom to share and change all +versions of a program--to make sure it remains free software for all its users. +We, the Free Software Foundation, use the GNU General Public License for most +of our software; it applies also to any other work released this way by its +authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for them if you wish), that you +receive source code or can get it if you want it, that you can change the +software or use pieces of it in new free programs, and that you know you can do +these things. + +To protect your rights, we need to prevent others from denying you these rights +or asking you to surrender the rights. Therefore, you have certain +responsibilities if you distribute copies of the software, or if you modify it: +responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must pass on to the recipients the same freedoms that you received. +You must make sure that they, too, receive or can get the source code. And you +must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: (1) assert +copyright on the software, and (2) offer you this License giving you legal +permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that +there is no warranty for this free software. For both users' and authors' sake, +the GPL requires that modified versions be marked as changed, so that their +problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified +versions of the software inside them, although the manufacturer can do so. This +is fundamentally incompatible with the aim of protecting users' freedom to +change the software. The systematic pattern of such abuse occurs in the area of +products for individuals to use, which is precisely where it is most +unacceptable. Therefore, we have designed this version of the GPL to prohibit +the practice for those products. If such problems arise substantially in other +domains, we stand ready to extend this provision to those domains in future +versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States +should not allow patents to restrict development and use of software on +general-purpose computers, but in those that do, we wish to avoid the special +danger that patents applied to a free program could make it effectively +proprietary. To prevent this, the GPL assures that patents cannot be used to +render the program non-free. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of + works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this License. + Each licensee is addressed as "you". "Licensees" and "recipients" may be + individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work in a + fashion requiring copyright permission, other than the making of an exact + copy. The resulting work is called a "modified version" of the earlier work + or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based on the + Program. + + To "propagate" a work means to do anything with it that, without permission, + would make you directly or secondarily liable for infringement under + applicable copyright law, except executing it on a computer or modifying a + private copy. Propagation includes copying, distribution (with or without + modification), making available to the public, and in some countries other + activities as well. + + To "convey" a work means any kind of propagation that enables other parties + to make or receive copies. Mere interaction with a user through a computer + network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" to the + extent that it includes a convenient and prominently visible feature that + (1) displays an appropriate copyright notice, and (2) tells the user that + there is no warranty for the work (except to the extent that warranties are + provided), that licensees may convey the work under this License, and how to + view a copy of this License. If the interface presents a list of user + commands or options, such as a menu, a prominent item in the list meets this + criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work for making + modifications to it. "Object code" means any non-source form of a work. + + A "Standard Interface" means an interface that either is an official + standard defined by a recognized standards body, or, in the case of + interfaces specified for a particular programming language, one that is + widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other than + the work as a whole, that (a) is included in the normal form of packaging a + Major Component, but which is not part of that Major Component, and (b) + serves only to enable use of the work with that Major Component, or to + implement a Standard Interface for which an implementation is available to + the public in source code form. A "Major Component", in this context, means + a major essential component (kernel, window system, and so on) of the + specific operating system (if any) on which the executable work runs, or a + compiler used to produce the work, or an object code interpreter used to run + it. + + The "Corresponding Source" for a work in object code form means all the + source code needed to generate, install, and (for an executable work) run + the object code and to modify the work, including scripts to control those + activities. However, it does not include the work's System Libraries, or + general-purpose tools or generally available free programs which are used + unmodified in performing those activities but which are not part of the + work. For example, Corresponding Source includes interface definition files + associated with source files for the work, and the source code for shared + libraries and dynamically linked subprograms that the work is specifically + designed to require, such as by intimate data communication or control flow + between those subprograms and other parts of the work. + + The Corresponding Source need not include anything that users can regenerate + automatically from other parts of the Corresponding Source. + + The Corresponding Source for a work in source code form is that same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of copyright + on the Program, and are irrevocable provided the stated conditions are met. + This License explicitly affirms your unlimited permission to run the + unmodified Program. The output from running a covered work is covered by + this License only if the output, given its content, constitutes a covered + work. This License acknowledges your rights of fair use or other equivalent, + as provided by copyright law. + + You may make, run and propagate covered works that you do not convey, + without conditions so long as your license otherwise remains in force. You + may convey covered works to others for the sole purpose of having them make + modifications exclusively for you, or provide you with facilities for + running those works, provided that you comply with the terms of this License + in conveying all material for which you do not control copyright. Those thus + making or running the covered works for you must do so exclusively on your + behalf, under your direction and control, on terms that prohibit them from + making any copies of your copyrighted material outside their relationship + with you. + + Conveying under any other circumstances is permitted solely under the + conditions stated below. Sublicensing is not allowed; section 10 makes it + unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological measure + under any applicable law fulfilling obligations under article 11 of the WIPO + copyright treaty adopted on 20 December 1996, or similar laws prohibiting or + restricting circumvention of such measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such circumvention is + effected by exercising rights under this License with respect to the covered + work, and you disclaim any intention to limit operation or modification of + the work as a means of enforcing, against the work's users, your or third + parties' legal rights to forbid circumvention of technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you receive + it, in any medium, provided that you conspicuously and appropriately publish + on each copy an appropriate copyright notice; keep intact all notices + stating that this License and any non-permissive terms added in accord with + section 7 apply to the code; keep intact all notices of the absence of any + warranty; and give all recipients a copy of this License along with the + Program. + + You may charge any price or no price for each copy that you convey, and you + may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to produce + it from the Program, in the form of source code under the terms of section + 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, + and giving a relevant date. + + b) The work must carry prominent notices stating that it is released + under this License and any conditions added under section 7. This + requirement modifies the requirement in section 4 to "keep intact all + notices". + + c) You must license the entire work, as a whole, under this License to + anyone who comes into possession of a copy. This License will therefore + apply, along with any applicable section 7 additional terms, to the whole + of the work, and all its parts, regardless of how they are packaged. This + License gives no permission to license the work in any other way, but it + does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your work need + not make them do so. + + A compilation of a covered work with other separate and independent works, + which are not by their nature extensions of the covered work, and which are + not combined with it such as to form a larger program, in or on a volume of + a storage or distribution medium, is called an "aggregate" if the + compilation and its resulting copyright are not used to limit the access or + legal rights of the compilation's users beyond what the individual works + permit. Inclusion of a covered work in an aggregate does not cause this + License to apply to the other parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms of + sections 4 and 5, provided that you also convey the machine-readable + Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium customarily used + for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a written + offer, valid for at least three years and valid for as long as you offer + spare parts or customer support for that product model, to give anyone + who possesses the object code either (1) a copy of the Corresponding + Source for all the software in the product that is covered by this + License, on a durable physical medium customarily used for software + interchange, for a price no more than your reasonable cost of physically + performing this conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written + offer to provide the Corresponding Source. This alternative is allowed + only occasionally and noncommercially, and only if you received the + object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place + (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no further + charge. You need not require recipients to copy the Corresponding Source + along with the object code. If the place to copy the object code is a + network server, the Corresponding Source may be on a different server + (operated by you or a third party) that supports equivalent copying + facilities, provided you maintain clear directions next to the object + code saying where to find the Corresponding Source. Regardless of what + server hosts the Corresponding Source, you remain obligated to ensure + that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you + inform other peers where the object code and Corresponding Source of the + work are being offered to the general public at no charge under + subsection 6d. + + A separable portion of the object code, whose source code is excluded from + the Corresponding Source as a System Library, need not be included in + conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any + tangible personal property which is normally used for personal, family, or + household purposes, or (2) anything designed or sold for incorporation into + a dwelling. In determining whether a product is a consumer product, doubtful + cases shall be resolved in favor of coverage. For a particular product + received by a particular user, "normally used" refers to a typical or common + use of that class of product, regardless of the status of the particular + user or of the way in which the particular user actually uses, or expects or + is expected to use, the product. A product is a consumer product regardless + of whether the product has substantial commercial, industrial or + non-consumer uses, unless such uses represent the only significant mode of + use of the product. + + "Installation Information" for a User Product means any methods, procedures, + authorization keys, or other information required to install and execute + modified versions of a covered work in that User Product from a modified + version of its Corresponding Source. The information must suffice to ensure + that the continued functioning of the modified object code is in no case + prevented or interfered with solely because modification has been made. + + If you convey an object code work under this section in, or with, or + specifically for use in, a User Product, and the conveying occurs as part of + a transaction in which the right of possession and use of the User Product + is transferred to the recipient in perpetuity or for a fixed term + (regardless of how the transaction is characterized), the Corresponding + Source conveyed under this section must be accompanied by the Installation + Information. But this requirement does not apply if neither you nor any + third party retains the ability to install modified object code on the User + Product (for example, the work has been installed in ROM). + + The requirement to provide Installation Information does not include a + requirement to continue to provide support service, warranty, or updates for + a work that has been modified or installed by the recipient, or for the User + Product in which it has been modified or installed. Access to a network may + be denied when the modification itself materially and adversely affects the + operation of the network or violates the rules and protocols for + communication across the network. + + Corresponding Source conveyed, and Installation Information provided, in + accord with this section must be in a format that is publicly documented + (and with an implementation available to the public in source code form), + and must require no special password or key for unpacking, reading or + copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this License + by making exceptions from one or more of its conditions. Additional + permissions that are applicable to the entire Program shall be treated as + though they were included in this License, to the extent that they are valid + under applicable law. If additional permissions apply only to part of the + Program, that part may be used separately under those permissions, but the + entire Program remains governed by this License without regard to the + additional permissions. + + When you convey a copy of a covered work, you may at your option remove any + additional permissions from that copy, or from any part of it. (Additional + permissions may be written to require their own removal in certain cases + when you modify the work.) You may place additional permissions on material, + added by you to a covered work, for which you have or can give appropriate + copyright permission. + + Notwithstanding any other provision of this License, for material you add to + a covered work, you may (if authorized by the copyright holders of that + material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms + of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author + attributions in that material or in the Appropriate Legal Notices + displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in reasonable + ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade + names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by + anyone who conveys the material (or modified versions of it) with + contractual assumptions of liability to the recipient, for any liability + that these contractual assumptions directly impose on those licensors and + authors. + + All other non-permissive additional terms are considered "further + restrictions" within the meaning of section 10. If the Program as you + received it, or any part of it, contains a notice stating that it is + governed by this License along with a term that is a further restriction, + you may remove that term. If a license document contains a further + restriction but permits relicensing or conveying under this License, you may + add to a covered work material governed by the terms of that license + document, provided that the further restriction does not survive such + relicensing or conveying. + + If you add terms to a covered work in accord with this section, you must + place, in the relevant source files, a statement of the additional terms + that apply to those files, or a notice indicating where to find the + applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the form of + a separately written license, or stated as exceptions; the above + requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly provided + under this License. Any attempt otherwise to propagate or modify it is void, + and will automatically terminate your rights under this License (including + any patent licenses granted under the third paragraph of section 11). + + However, if you cease all violation of this License, then your license from + a particular copyright holder is reinstated (a) provisionally, unless and + until the copyright holder explicitly and finally terminates your license, + and (b) permanently, if the copyright holder fails to notify you of the + violation by some reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is reinstated + permanently if the copyright holder notifies you of the violation by some + reasonable means, this is the first time you have received notice of + violation of this License (for any work) from that copyright holder, and you + cure the violation prior to 30 days after your receipt of the notice. + + Termination of your rights under this section does not terminate the + licenses of parties who have received copies or rights from you under this + License. If your rights have been terminated and not permanently reinstated, + you do not qualify to receive new licenses for the same material under + section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or run a + copy of the Program. Ancillary propagation of a covered work occurring + solely as a consequence of using peer-to-peer transmission to receive a copy + likewise does not require acceptance. However, nothing other than this + License grants you permission to propagate or modify any covered work. These + actions infringe copyright if you do not accept this License. Therefore, by + modifying or propagating a covered work, you indicate your acceptance of + this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically receives a + license from the original licensors, to run, modify and propagate that work, + subject to this License. You are not responsible for enforcing compliance by + third parties with this License. + + An "entity transaction" is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a covered work + results from an entity transaction, each party to that transaction who + receives a copy of the work also receives whatever licenses to the work the + party's predecessor in interest had or could give under the previous + paragraph, plus a right to possession of the Corresponding Source of the + work from the predecessor in interest, if the predecessor has it or can get + it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the rights + granted or affirmed under this License. For example, you may not impose a + license fee, royalty, or other charge for exercise of rights granted under + this License, and you may not initiate litigation (including a cross-claim + or counterclaim in a lawsuit) alleging that any patent claim is infringed by + making, using, selling, offering for sale, or importing the Program or any + portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this License + of the Program or a work on which the Program is based. The work thus + licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims owned or + controlled by the contributor, whether already acquired or hereafter + acquired, that would be infringed by some manner, permitted by this License, + of making, using, or selling its contributor version, but do not include + claims that would be infringed only as a consequence of further modification + of the contributor version. For purposes of this definition, "control" + includes the right to grant patent sublicenses in a manner consistent with + the requirements of this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free patent + license under the contributor's essential patent claims, to make, use, sell, + offer for sale, import and otherwise run, modify and propagate the contents + of its contributor version. + + In the following three paragraphs, a "patent license" is any express + agreement or commitment, however denominated, not to enforce a patent (such + as an express permission to practice a patent or covenant not to sue for + patent infringement). To "grant" such a patent license to a party means to + make such an agreement or commitment not to enforce a patent against the + party. + + If you convey a covered work, knowingly relying on a patent license, and the + Corresponding Source of the work is not available for anyone to copy, free + of charge and under the terms of this License, through a publicly available + network server or other readily accessible means, then you must either (1) + cause the Corresponding Source to be so available, or (2) arrange to deprive + yourself of the benefit of the patent license for this particular work, or + (3) arrange, in a manner consistent with the requirements of this License, + to extend the patent license to downstream recipients. "Knowingly relying" + means you have actual knowledge that, but for the patent license, your + conveying the covered work in a country, or your recipient's use of the + covered work in a country, would infringe one or more identifiable patents + in that country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or arrangement, + you convey, or propagate by procuring conveyance of, a covered work, and + grant a patent license to some of the parties receiving the covered work + authorizing them to use, propagate, modify or convey a specific copy of the + covered work, then the patent license you grant is automatically extended to + all recipients of the covered work and works based on it. + + A patent license is "discriminatory" if it does not include within the scope + of its coverage, prohibits the exercise of, or is conditioned on the + non-exercise of one or more of the rights that are specifically granted + under this License. You may not convey a covered work if you are a party to + an arrangement with a third party that is in the business of distributing + software, under which you make payment to the third party based on the + extent of your activity of conveying the work, and under which the third + party grants, to any of the parties who would receive the covered work from + you, a discriminatory patent license (a) in connection with copies of the + covered work conveyed by you (or copies made from those copies), or (b) + primarily for and in connection with specific products or compilations that + contain the covered work, unless you entered into that arrangement, or that + patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting any + implied license or other defenses to infringement that may otherwise be + available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot convey a + covered work so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you may + not convey it at all. For example, if you agree to terms that obligate you + to collect a royalty for further conveying from those to whom you convey the + Program, the only way you could satisfy both those terms and this License + would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have permission to + link or combine any covered work with a work licensed under version 3 of the + GNU Affero General Public License into a single combined work, and to convey + the resulting work. The terms of this License will continue to apply to the + part which is the covered work, but the special requirements of the GNU + Affero General Public License, section 13, concerning interaction through a + network will apply to the combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of the + GNU General Public License from time to time. Such new versions will be + similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies that a certain numbered version of the GNU General Public License + "or any later version" applies to it, you have the option of following the + terms and conditions either of that numbered version or of any later version + published by the Free Software Foundation. If the Program does not specify a + version number of the GNU General Public License, you may choose any version + ever published by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future versions of + the GNU General Public License can be used, that proxy's public statement of + acceptance of a version permanently authorizes you to choose that version + for the Program. + + Later license versions may give you additional or different permissions. + However, no additional obligations are imposed on any author or copyright + holder as a result of your choosing to follow a later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE + LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR + OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, + EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. + SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY + SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL + ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE + PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY + GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE + OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA + OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), + EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF + SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided above + cannot be given local legal effect according to their terms, reviewing + courts shall apply local law that most closely approximates an absolute + waiver of all civil liability in connection with the Program, unless a + warranty or assumption of liability accompanies a copy of the Program in + return for a fee. END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively state the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + +<one line to give the program's name and a brief idea of what it does.> + +Copyright (C) <year> <name of author> + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <http s ://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like +this when it starts in an interactive mode: + +<program> Copyright (C) <year> <name of author> + +This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + +This is free software, and you are welcome to redistribute it under certain +conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands might +be different; for a GUI interface, you would use an "about box". + +You should also get your employer (if you work as a programmer) or school, if +any, to sign a "copyright disclaimer" for the program, if necessary. For more +information on this, and how to apply and follow the GNU GPL, see <http s +://www.gnu.org/licenses/>. + +The GNU General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may consider +it more useful to permit linking proprietary applications with the library. If +this is what you want to do, use the GNU Lesser General Public License instead +of this License. But first, please read <http s ://www.gnu.org/ licenses +/why-not-lgpl.html>. + +------ + +** FFmpeg; version 4.2.3 -- http://ffmpeg.org/ +- + +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts as the +successor of the GNU Library Public License, version 2, hence the version +number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Lesser General Public License, applies to some specially +designated software packages--typically libraries--of the Free Software +Foundation and other authors who decide to use it. You can use it too, but we +suggest you first think carefully about whether this license or the ordinary +General Public License is the better strategy to use in any particular case, +based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. +Our General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you +wish); that you receive source code or can get it if you want it; that you can +change the software and use pieces of it in new free programs; and that you are +informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors +to deny you these rights or to ask you to surrender these rights. These +restrictions translate to certain responsibilities for you if you distribute +copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a +fee, you must give the recipients all the rights that we gave you. You must +make sure that they, too, receive or can get the source code. If you link other +code with the library, you must provide complete object files to the +recipients, so that they can relink them with the library after making changes +to the library and recompiling it. And you must show them these terms so they +know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, +and (2) we offer you this license, which gives you legal permission to copy, +distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no +warranty for the free library. Also, if the library is modified by someone else +and passed on, the recipients should know that what they have is not the +original version, so that the original author's reputation will not be affected +by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free +program. We wish to make sure that a company cannot effectively restrict the +users of a free program by obtaining a restrictive license from a patent +holder. Therefore, we insist that any patent license obtained for a version of +the library must be consistent with the full freedom of use specified in this +license. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License. This license, the GNU Lesser General Public License, +applies to certain designated libraries, and is quite different from the +ordinary General Public License. We use this license for certain libraries in +order to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared +library, the combination of the two is legally speaking a combined work, a +derivative of the original library. The ordinary General Public License +therefore permits such linking only if the entire combination fits its criteria +of freedom. The Lesser General Public License permits more lax criteria for +linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less +to protect the user's freedom than the ordinary General Public License. It also +provides other free software developers Less of an advantage over competing +non-free programs. These disadvantages are the reason we use the ordinary +General Public License for many libraries. However, the Lesser license provides +advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the +widest possible use of a certain library, so that it becomes a de-facto +standard. To achieve this, non-free programs must be allowed to use the +library. A more frequent case is that a free library does the same job as +widely used non-free libraries. In this case, there is little to gain by +limiting the free library to free software only, so we use the Lesser General +Public License. + +In other cases, permission to use a particular library in non-free programs +enables a greater number of people to use a large body of free software. For +example, permission to use the GNU C Library in non-free programs enables many +more people to use the whole GNU operating system, as well as its variant, the +GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' +freedom, it does ensure that the user of a program that is linked with the +Library has the freedom and the wherewithal to run that program using a +modified version of the Library. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, whereas the latter must be combined with the library in order +to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other program + which contains a notice placed by the copyright holder or other authorized + party saying it may be distributed under the terms of this Lesser General + Public License (also called "this License"). Each licensee is addressed as + "you". + + A "library" means a collection of software functions and/or data prepared so + as to be conveniently linked with application programs (which use some of + those functions and data) to form executables. + + The "Library", below, refers to any such software library or work which has + been distributed under these terms. A "work based on the Library" means + either the Library or any derivative work under copyright law: that is to + say, a work containing the Library or a portion of it, either verbatim or + with modifications and/or translated straightforwardly into another + language. (Hereinafter, translation is included without limitation in the + term "modification".) + + "Source code" for a work means the preferred form of the work for making + modifications to it. For a library, complete source code means all the + source code for all modules it contains, plus any associated interface + definition files, plus the scripts used to control compilation and + installation of the library. + + Activities other than copying, distribution and modification are not covered + by this License; they are outside its scope. The act of running a program + using the Library is not restricted, and output from such a program is + covered only if its contents constitute a work based on the Library + (independent of the use of the Library in a tool for writing it). Whether + that is true depends on what the Library does and what the program that uses + the Library does. + + 1. You may copy and distribute verbatim copies of the Library's complete + source code as you receive it, in any medium, provided that you + conspicuously and appropriately publish on each copy an appropriate + copyright notice and disclaimer of warranty; keep intact all the notices + that refer to this License and to the absence of any warranty; and + distribute a copy of this License along with the Library. + + You may charge a fee for the physical act of transferring a copy, and you + may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Library or any portion of it, + thus forming a work based on the Library, and copy and distribute such + modifications or work under the terms of Section 1 above, provided that you + also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices stating + that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no charge to + all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a table + of data to be supplied by an application program that uses the facility, + other than as an argument passed when the facility is invoked, then you + must make a good faith effort to ensure that, in the event an application + does not supply such function or table, the facility still operates, and + performs whatever part of its purpose remains meaningful. + + (For example, a function in a library to compute square roots has a purpose + that is entirely well-defined independent of the application. Therefore, + Subsection 2d requires that any application-supplied function or table used + by this function must be optional: if the application does not supply it, + the square root function must still compute square roots.) + + These requirements apply to the modified work as a whole. If identifiable + sections of that work are not derived from the Library, and can be + reasonably considered independent and separate works in themselves, then + this License, and its terms, do not apply to those sections when you + distribute them as separate works. But when you distribute the same sections + as part of a whole which is a work based on the Library, the distribution of + the whole must be on the terms of this License, whose permissions for other + licensees extend to the entire whole, and thus to each and every part + regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest your + rights to work written entirely by you; rather, the intent is to exercise + the right to control the distribution of derivative or collective works + based on the Library. + + In addition, mere aggregation of another work not based on the Library with + the Library (or with a work based on the Library) on a volume of a storage + or distribution medium does not bring the other work under the scope of this + License. + + 3. You may opt to apply the terms of the ordinary GNU General Public License + instead of this License to a given copy of the Library. To do this, you must + alter all the notices that refer to this License, so that they refer to the + ordinary GNU General Public License, version 2, instead of to this License. + (If a newer version than version 2 of the ordinary GNU General Public + License has appeared, then you can specify that version instead if you + wish.) Do not make any other change in these notices. + + Once this change is made in a given copy, it is irreversible for that copy, + so the ordinary GNU General Public License applies to all subsequent copies + and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of the Library + into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or derivative of + it, under Section 2) in object code or executable form under the terms of + Sections 1 and 2 above provided that you accompany it with the complete + corresponding machine-readable source code, which must be distributed under + the terms of Sections 1 and 2 above on a medium customarily used for + software interchange. + + If distribution of object code is made by offering access to copy from a + designated place, then offering equivalent access to copy the source code + from the same place satisfies the requirement to distribute the source code, + even though third parties are not compelled to copy the source along with + the object code. + + 5. A program that contains no derivative of any portion of the Library, but + is designed to work with the Library by being compiled or linked with it, is + called a "work that uses the Library". Such a work, in isolation, is not a + derivative work of the Library, and therefore falls outside the scope of + this License. + + However, linking a "work that uses the Library" with the Library creates an + executable that is a derivative of the Library (because it contains portions + of the Library), rather than a "work that uses the library". The executable + is therefore covered by this License. Section 6 states terms for + distribution of such executables. + + When a "work that uses the Library" uses material from a header file that is + part of the Library, the object code for the work may be a derivative work + of the Library even though the source code is not. Whether this is true is + especially significant if the work can be linked without the Library, or if + the work is itself a library. The threshold for this to be true is not + precisely defined by law. + + If such an object file uses only numerical parameters, data structure + layouts and accessors, and small macros and small inline functions (ten + lines or less in length), then the use of the object file is unrestricted, + regardless of whether it is legally a derivative work. (Executables + containing this object code plus portions of the Library will still fall + under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may distribute + the object code for the work under the terms of Section 6. Any executables + containing that work also fall under Section 6, whether or not they are + linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or link a + "work that uses the Library" with the Library to produce a work containing + portions of the Library, and distribute that work under terms of your + choice, provided that the terms permit modification of the work for the + customer's own use and reverse engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the Library + is used in it and that the Library and its use are covered by this License. + You must supply a copy of this License. If the work during execution + displays copyright notices, you must include the copyright notice for the + Library among them, as well as a reference directing the user to the copy of + this License. Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable + source code for the Library including whatever changes were used in the + work (which must be distributed under Sections 1 and 2 above); and, if + the work is an executable linked with the Library, with the complete + machine-readable "work that uses the Library", as object code and/or + source code, so that the user can modify the Library and then relink to + produce a modified executable containing the modified Library. (It is + understood that the user who changes the contents of definitions files in + the Library will not necessarily be able to recompile the application to + use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the Library. + A suitable mechanism is one that (1) uses at run time a copy of the + library already present on the user's computer system, rather than + copying library functions into the executable, and (2) will operate + properly with a modified version of the library, if the user installs + one, as long as the modified version is interface-compatible with the + version that the work was made with. + + c) Accompany the work with a written offer, valid for at least three + years, to give the same user the materials specified in Subsection 6a, + above, for a charge no more than the cost of performing this + distribution. + + d) If distribution of the work is made by offering access to copy from a + designated place, offer equivalent access to copy the above specified + materials from the same place. + + e) Verify that the user has already received a copy of these materials or + that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the Library" + must include any data and utility programs needed for reproducing the + executable from it. However, as a special exception, the materials to be + distributed need not include anything that is normally distributed (in + either source or binary form) with the major components (compiler, kernel, + and so on) of the operating system on which the executable runs, unless that + component itself accompanies the executable. + + It may happen that this requirement contradicts the license restrictions of + other proprietary libraries that do not normally accompany the operating + system. Such a contradiction means you cannot use both them and the Library + together in an executable that you distribute. + + 7. You may place library facilities that are a work based on the Library + side-by-side in a single library together with other library facilities not + covered by this License, and distribute such a combined library, provided + that the separate distribution of the work based on the Library and of the + other library facilities is otherwise permitted, and provided that you do + these two things: + + a) Accompany the combined library with a copy of the same work based on + the Library, uncombined with any other library facilities. This must be + distributed under the terms of the Sections above. + + b) Give prominent notice with the combined library of the fact that part + of it is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute the + Library except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense, link with, or distribute the Library + is void, and will automatically terminate your rights under this License. + However, parties who have received copies, or rights, from you under this + License will not have their licenses terminated so long as such parties + remain in full compliance. + + 9. You are not required to accept this License, since you have not signed + it. However, nothing else grants you permission to modify or distribute the + Library or its derivative works. These actions are prohibited by law if you + do not accept this License. Therefore, by modifying or distributing the + Library (or any work based on the Library), you indicate your acceptance of + this License to do so, and all its terms and conditions for copying, + distributing or modifying the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the + Library), the recipient automatically receives a license from the original + licensor to copy, distribute, link with or modify the Library subject to + these terms and conditions. You may not impose any further restrictions on + the recipients' exercise of the rights granted herein. You are not + responsible for enforcing compliance by third parties with this License. + + 11. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute so + as to satisfy simultaneously your obligations under this License and any + other pertinent obligations, then as a consequence you may not distribute + the Library at all. For example, if a patent license would not permit + royalty-free redistribution of the Library by all those who receive copies + directly or indirectly through you, then the only way you could satisfy both + it and this License would be to refrain entirely from distribution of the + Library. + + If any portion of this section is held invalid or unenforceable under any + particular circumstance, the balance of the section is intended to apply, + and the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any patents + or other property right claims or to contest validity of any such claims; + this section has the sole purpose of protecting the integrity of the free + software distribution system which is implemented by public license + practices. Many people have made generous contributions to the wide range of + software distributed through that system in reliance on consistent + application of that system; it is up to the author/donor to decide if he or + she is willing to distribute software through any other system and a + licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be a + consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in certain + countries either by patents or by copyrighted interfaces, the original + copyright holder who places the Library under this License may add an + explicit geographical distribution limitation excluding those countries, so + that distribution is permitted only in or among countries not thus excluded. + In such case, this License incorporates the limitation as if written in the + body of this License. + + 13. The Free Software Foundation may publish revised and/or new versions of + the Lesser General Public License from time to time. Such new versions will + be similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the Library + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and conditions + either of that version or of any later version published by the Free + Software Foundation. If the Library does not specify a license version + number, you may choose any version ever published by the Free Software + Foundation. + + 14. If you wish to incorporate parts of the Library into other free programs + whose distribution conditions are incompatible with these, write to the + author to ask for permission. For software which is copyrighted by the Free + Software Foundation, write to the Free Software Foundation; we sometimes + make exceptions for this. Our decision will be guided by the two goals of + preserving the free status of all derivatives of our free software and of + promoting the sharing and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR + THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO + THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY + PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR + CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, + INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING + OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO + LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR + THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER + SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible +use to the public, we recommend making it free software that everyone can +redistribute and change. You can do so by permitting redistribution under these +terms (or, alternatively, under the terms of the ordinary General Public +License). + +To apply these terms, attach the following notices to the library. It is safest +to attach them to the start of each source file to most effectively convey the +exclusion of warranty; and each file should have at least the "copyright" line +and a pointer to where the full notice is found. + +<one line to give the library's name and an idea of what it does.> + +Copyright (C) <year> <name of author> + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 2.1 of the License, or (at your option) any +later version. + +This library is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this library; if not, write to the Free Software Foundation, Inc., 51 +Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the library, if necessary. Here is +a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +< signature of Ty Coon > , 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! + +------ + +** Libsndfile; version 1.0.28 -- http://www.mega-nerd.com/libsndfile/ +Copyright (C) 2011-2016 Erik de Castro Lopo <erikd@mega-nerd.com> + +GNU LESSER GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. <http s ://fsf.org/> + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the terms +and conditions of version 3 of the GNU General Public License, supplemented by +the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser + General Public License, and the "GNU GPL" refers to version 3 of the GNU + General Public License. + + "The Library" refers to a covered work governed by this License, other + than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided by + the Library, but which is not otherwise based on the Library. Defining a + subclass of a class defined by the Library is deemed a mode of using an + interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an + Application with the Library. The particular version of the Library with + which the Combined Work was made is also called the "Linked Version". + + The "Minimal Corresponding Source" for a Combined Work means the + Corresponding Source for the Combined Work, excluding any source code for + portions of the Combined Work that, considered in isolation, are based on + the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the object + code and/or source code for the Application, including any data and + utility programs needed for reproducing the Combined Work from the + Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License without + being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a facility + refers to a function or data to be supplied by an Application that uses the + facility (other than as an argument passed when the facility is invoked), + then you may convey a copy of the modified version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the function or + data, the facility still operates, and performs whatever part of its + purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of this + License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from a + header file that is part of the Library. You may convey such object code + under terms of your choice, provided that, if the incorporated material is + not limited to numerical parameters, data structure layouts and accessors, + or small macros, inline functions and templates (ten or fewer lines in + length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are covered by + this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, taken + together, effectively do not restrict modification of the portions of the + Library contained in the Combined Work and reverse engineering for debugging + such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that the + Library is used in it and that the Library and its use are covered by + this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this + license document. + + c) For a Combined Work that displays copyright notices during execution, + include the copyright notice for the Library among these notices, as well + as a reference directing the user to the copies of the GNU GPL and this + license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form suitable + for, and under terms that permit, the user to recombine or relink the + Application with a modified version of the Linked Version to produce a + modified Combined Work, in the manner specified by section 6 of the + GNU GPL for conveying Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time a copy + of the Library already present on the user's computer system, and (b) + will operate properly with a modified version of the Library that is + interface-compatible with the Linked Version. + + e) Provide Installation Information, but only if you would otherwise be + required to provide such information under section 6 of the GNU GPL, and + only to the extent that such information is necessary to install and + execute a modified version of the Combined Work produced by recombining + or relinking the Application with a modified version of the Linked + Version. (If you use option 4d0, the Installation Information must + accompany the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL for + conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the Library side + by side in a single library together with other library facilities that are + not Applications and are not covered by this License, and convey such a + combined library under terms of your choice, if you do both of the + following: + + a) Accompany the combined library with a copy of the same work based on + the Library, uncombined with any other library facilities, conveyed under + the terms of this License. + + b) Give prominent notice with the combined library that part of it is a + work based on the Library, and explaining where to find the accompanying + uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions of the + GNU Lesser General Public License from time to time. Such new versions will + be similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the Library as you + received it specifies that a certain numbered version of the GNU Lesser + General Public License "or any later version" applies to it, you have the + option of following the terms and conditions either of that published + version or of any later version published by the Free Software Foundation. + If the Library as you received it does not specify a version number of the + GNU Lesser General Public License, you may choose any version of the GNU + Lesser General Public License ever published by the Free Software + Foundation. + + If the Library as you received it specifies that a proxy can decide whether + future versions of the GNU Lesser General Public License shall apply, that + proxy's public statement of acceptance of any version is permanent + authorization for you to choose that version for the Library. + +------ + +** LIBPNG; version 1.6.37 -- http://prdownloads.sourceforge.net/libpng +* Copyright (c) 1995-2019 The PNG Reference Library Authors. + * Copyright (c) 2018-2019 Cosmin Truta. + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. + * Copyright (c) 1996-1997 Andreas Dilger. + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + +This copy of the libpng notices is provided for your convenience. In case of +any discrepancy between this copy and the notices in the file png.h that is +included in the libpng distribution, the latter shall prevail. + +COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: + +If you modify libpng you may insert additional notices immediately following +this sentence. + +This code is released under the libpng license. + +libpng versions 1.2.6, August 15, 2004, through 1.4.5, December 9, 2010, are +Copyright (c) 2004, 2006-2010 Glenn Randers-Pehrson, and are distributed +according to the same disclaimer and license as libpng-1.2.5 with the following +individual added to the list of Contributing Authors + +Cosmin Truta + +libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are + +Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are distributed according to +the same disclaimer and license as libpng-1.0.6 with the following individuals +added to the list of Contributing Authors + +Simon-Pierre Cadieux + +Eric S. Raymond + +Gilles Vollant + +and with the following additions to the disclaimer: + +There is no warranty against interference with your enjoyment of the library or +against infringement. There is no warranty that our efforts or the library will +fulfill any of your particular purposes or needs. This library is provided with +all faults, and the entire risk of satisfactory quality, performance, accuracy, +and effort is with the user. + +libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + +Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are distributed according +to the same disclaimer and license as libpng-0.96, with the following +individuals added to the list of Contributing Authors: + +Tom Lane + +Glenn Randers-Pehrson + +Willem van Schaik + +libpng versions 0.89, June 1996, through 0.96, May 1997, are + +Copyright (c) 1996, 1997 Andreas Digger + +Distributed according to the same disclaimer and license as libpng-0.88, with +the following individuals added to the list of Contributing Authors: + +John Bowler + +Kevin Bracey + +Sam Bushell + +Magnus Holmgren + +Greg Roelofs + +Tom Tanner + +libpng versions 0.5, May 1995, through 0.88, January 1996, are + +Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. + +For the purposes of this copyright and license, "Contributing Authors" is +defined as the following set of individuals: + +Andreas Dilger + +Dave Martindale + +Guy Eric Schalnat + +Paul Schmidt + +Tim Wegner + +The PNG Reference Library is supplied "AS IS". The Contributing Authors and +Group 42, Inc. disclaim all warranties, expressed or implied, including, +without limitation, the warranties of merchantability and of fitness for any +purpose. The Contributing Authors and Group 42, Inc. assume no liability for +direct, indirect, incidental, special, exemplary, or consequential damages, +which may result from the use of the PNG Reference Library, even if advised of +the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this source +code, or portions hereof, for any purpose, without fee, subject to the +following restrictions: + + 1. The origin of this source code must not be misrepresented. + + 2. Altered versions must be plainly marked as such and must not be + misrepresented as being the original source. + + 3. This Copyright notice may not be removed or altered from any source or + altered source distribution. + +The Contributing Authors and Group 42, Inc. specifically permit, without fee, +and encourage the use of this source code as a component to supporting the PNG +file format in commercial products. If you use this source code in a product, +acknowledgment is not required but would be appreciated. + +A "png_get_copyright" function is available, for convenient use in "about" +boxes and the like: + +printf("%s",png_get_copyright(NULL)); + +Also, the PNG logo (in PNG format, of course) is supplied in the files +"pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + +Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a +certification mark of the Open Source Initiative. + +Glenn Randers-Pehrson + +glennrp at users.sourceforge.net + +December 9, 2010 + +------ + +** Libxml2; version 2.9.10 -- http://xmlsoft.org/ +Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved. +** Mesa 3D; version 18.3.1 -- https://www.mesa3d.org/ +Copyright (C) 1999-2007 Brian Paul All Rights Reserved. +** OPENCollada; version 1.6.68 -- https://github.com/KhronosGroup/OpenCOLLADA +Copyright (c) 2008-2009 NetAllied Systems GmbH +** PugiXML; version 1.10 -- http://pugixml.org/ +Copyright (c) 2006-2020 Arseny Kapoulkine +** QuadriFlow; version 27a6867 -- https://github.com/hjwdzh/QuadriFlow +Copyright (c) 2018 Jingwei Huang, Yichao Zhou, Matthias Niessner, +Jonathan Shewchuk and Leonidas Guibas. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------ + +** NanoVDB; version e62f7a0bf1e27397223c61ddeaaf57edf111b77f -- +https://github.com/AcademySoftwareFoundation/openvdb +Copyright Contributors to the OpenVDB Project +** OpenVDB; version 7.0.0 -- http://www.openvdb.org/ +Copyright Contributors to the OpenVDB Project + +Mozilla Public License Version 2.0 + + 1. Definitions + + 1.1. "Contributor" means each individual or legal entity that creates, + contributes to the creation of, or owns Covered Software. + + 1.2. "Contributor Version" means the combination of the Contributions of + others (if any) used by a Contributor and that particular Contributor's + Contribution. + + 1.3. "Contribution" means Covered Software of a particular Contributor. + + 1.4. "Covered Software" means Source Code Form to which the initial + Contributor has attached the notice in Exhibit A, the Executable Form of + such Source Code Form, and Modifications of such Source Code Form, in + each case including portions thereof. + + 1.5. "Incompatible With Secondary Licenses" means + + (a) that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + + 1.6. "Executable Form" means any form of the work other than Source Code + Form. + + 1.7. "Larger Work" means a work that combines Covered Software with other + material, in a separate file or files, that is not Covered Software. + + 1.8. "License" means this document. + + 1.9. "Licensable" means having the right to grant, to the maximum extent + possible, whether at the time of the initial grant or subsequently, any + and all of the rights conveyed by this License. + + 1.10. "Modifications" means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + + 1.11. "Patent Claims" of a Contributor means any patent claim(s), + including without limitation, method, process, and apparatus claims, in + any patent Licensable by such Contributor that would be infringed, but + for the grant of the License, by the making, using, selling, offering for + sale, having made, import, or transfer of either its Contributions or its + Contributor Version. + + 1.12. "Secondary License" means either the GNU General Public License, + Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU + Affero General Public License, Version 3.0, or any later versions of + those licenses. + + 1.13. "Source Code Form" means the form of the work preferred for making + modifications. + + 1.14. "You" (or "Your") means an individual or a legal entity exercising + rights under this License. For legal entities, "You" includes any entity + that controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct or + indirect, to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent (50%) + of the outstanding shares or beneficial ownership of such entity. + + 2. License Grants and Conditions + + 2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by such Contributor to use, reproduce, make + available, modify, display, perform, distribute, and otherwise exploit + its Contributions, either on an unmodified basis, with Modifications, + or as part of a Larger Work; and + + (b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + + 2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + + 2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + (a) for any code that a Contributor has removed from Covered Software; + or + + (b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + (c) under Patent Claims infringed by Covered Software in the absence + of its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + + 2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + + 2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + + 2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + + 2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + 3. Responsibilities + + 3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + + 3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + (a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more than + the cost of distribution to the recipient; and + + (b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + + 3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + + 3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + + 3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + + 4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this + License to the maximum extent possible; and (b) describe the limitations and + the code they affect. Such description must be placed in a text file + included with all distributions of the Covered Software under this License. + Except to the extent prohibited by statute or regulation, such description + must be sufficiently detailed for a recipient of ordinary skill to be able + to understand it. + + 5. Termination + + 5.1. The rights granted under this License will terminate automatically + if You fail to comply with any of its terms. However, if You become + compliant, then the rights granted under this License from a particular + Contributor are reinstated (a) provisionally, unless and until such + Contributor explicitly and finally terminates Your grants, and (b) on an + ongoing basis, if such Contributor fails to notify You of the + non-compliance by some reasonable means prior to 60 days after You have + come back into compliance. Moreover, Your grants from a particular + Contributor are reinstated on an ongoing basis if such Contributor + notifies You of the non-compliance by some reasonable means, this is the + first time You have received notice of non-compliance with this License + from such Contributor, and You become compliant prior to 30 days after + Your receipt of the notice. + + 5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + + 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end + user license agreements (excluding distributors and resellers) which have + been validly granted by You or Your distributors under this License prior + to termination shall survive termination. + + 6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of + this License. No use of any Covered Software is authorized under this + License except under this disclaimer. + + 7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + + 8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party's ability to bring cross-claims or counter-claims. + + 9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to + construe this License against a Contributor. + + 10. Versions of the License + + 10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + + 10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + + 10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + + 10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses + + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. Exhibit A + - Source Code Form License Notice + +This Source Code Form is subject to the terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not distributed with this file, You can obtain +one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + +This Source Code Form is "Incompatible With Secondary Licenses", as defined by +the Mozilla Public License, v. 2.0. + +------ + +** Bullet Continuous Collision Detection and Physics Library; version 3.07 -- +http://continuousphysics.com/Bullet/ +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ +** SDL; version 2.0.12 -- https://www.libsdl.org +Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> +** zlib; version 1.2.11 -- https://zlib.net +Copyright (C) 1995-2017 Jean-loup Gailly + +zlib License Copyright (c) <year> <copyright holders> + +This software is provided 'as-is', without any express or implied warranty. In +no event will the authors be held liable for any damages arising from the use +of this software. + +Permission is granted to anyone to use this software for any purpose, including +commercial applications, and to alter it and redistribute it freely, subject to +the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be appreciated + but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +------ + +** LibTIFF; version 4.1.0 -- http://www.libtiff.org/ +Copyright (c) 1988-1997 Sam Leffler +Copyright (c) 1991-1997 Silicon Graphics, Inc. + +Copyright (c) 1988-1997 Sam Leffler + +Copyright (c) 1991-1997 Silicon Graphics, Inc. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that (i) +the above copyright notices and this permission notice appear in all copies of +the software and related documentation, and (ii) the names of Sam Leffler and +Silicon Graphics may not be used in any advertising or publicity relating to +the software without the specific, prior written permission of Sam Leffler and +Silicon Graphics. + +THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, +IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, +INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED +OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +------ + +** The LLVM Compiler Infrastructure; version 9.0.1 -- +https://github.com/llvm/llvm-project/ +University of Illinois/NCSA +Open Source License + +Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. +All rights reserved. + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +------ + +** OpenSubdiv; version 3.4.3 -- http://graphics.pixar.com/opensubdiv +Copyright 2013 Pixar +** Universal Scene Description; version 20.05 -- http://www.openusd.org/ +Copyright 2016 Pixar + +Licensed under the Apache License, Version 2.0 (the "Apache License") with the +following modification; you may not use this file except in compliance with the +Apache License and the following modification to it: + +Section 6. Trademarks. is deleted and replaced with: + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor and its affiliates, +except as required to comply with Section 4(c) of the License and to reproduce +the content of the NOTICE file. + +------ + +** libjpeg-turbo; version 2.0.4 -- +https://github.com/libjpeg-turbo/libjpeg-turbo/ +Copyright (C)2009-2020 D. R. Commander. All Rights Reserved. +Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. +Neither the name of the libjpeg-turbo Project nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------ + +** Boost C++ Libraries; version 1.70.0 -- https://www.boost.org/ +- + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +------ + +** Alembic; version 1.7.12 -- https://github.com/alembic/alembic +TM & © 2009-2015 Lucasfilm Entertainment Company Ltd. or Lucasfilm Ltd. +All rights reserved. + +Industrial Light & Magic, ILM and the Bulb and Gear design logo are all +registered trademarks or service marks of Lucasfilm Ltd. + +© 2009-2015 Sony Pictures Imageworks Inc. All rights reserved. + +TM & © 2009-2015 Lucasfilm Entertainment Company Ltd. or Lucasfilm Ltd. +All rights reserved. + +Industrial Light & Magic, ILM and the Bulb and Gear design logo are all +registered trademarks or service marks of Lucasfilm Ltd. + +© 2009-2015 Sony Pictures Imageworks Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. +* Neither the name of Industrial Light & Magic nor the names of +its contributors may be used to endorse or promote products derived +from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +------------------------------------------------------------------------------- + +ALEMBIC ATTACHMENT A — +REQUIRED NOTICES FOR DISTRIBUTION + +The Alembic Software is distributed along with certain third party +components licensed under various open source software licenses ("Open +Source Components"). In addition to the warranty disclaimers contained +in the open source licenses found below, Industrial Light & Magic, a +division of Lucasfilm Entertainment Company Ltd. ("ILM") makes the +following disclaimers regarding the Open Source Components on behalf of +itself, the copyright holders, contributors, and licensors of such Open +Source Components: + +TO THE FULLEST EXTENT PERMITTED UNDER APPLICABLE LAW, THE OPEN SOURCE +COMPONENTS ARE PROVIDED BY THE COPYRIGHT HOLDERS, CONTRIBUTORS, +LICENSORS, AND ILM "AS IS" AND ANY REPRESENTATIONS OR WARRANTIES OF ANY +KIND, WHETHER ORAL OR WRITTEN, WHETHER EXPRESS, IMPLIED, OR ARISING BY +STATUTE, CUSTOM, COURSE OF DEALING, OR TRADE USAGE, INCLUDING WITHOUT +LIMITATION THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR +A PARTICULAR PURPOSE, AND NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT +WILL THE COPYRIGHT OWNER, CONTRIBUTORS, LICENSORS, OR ILM AND/OR ITS +AFFILIATES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE OPEN +SOURCE COMPONENTS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Boost C++ Libraries +------------------------------------------------------------------------ + +Boost Software License – Version 1.0 August 17th, 2003 Permission is +hereby granted, free of charge, to any person or organization obtaining +a copy of the software and accompanying documentation covered by this +license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of +the Software, and to permit third-parties to whom the Software is +furnished to do so, all subject to the following: + +The copyright notices in the Software and this entire statement, +including the above license grant, this restriction and the following +disclaimer, must be included in all copies of the Software, in whole or +in part, and all derivative works of the Software, unless such copies or +derivative works are solely in the form of machine-executable object +code generated by a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND +NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE +DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, +WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------ + +** WC Width; version 2007-05-26 -- http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c +Markus Kuhn -- 2007-05-26 (Unicode 5.0) + +Permission to use, copy, modify, and distribute this software +for any purpose and without fee is hereby granted. The author +disclaims all warranties with regard to this software. + +------ + +** Python; version 3.7.7 -- https://www.python.org +Copyright (c) 2001-2020 Python Software Foundation. All rights reserved. + +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see http://www.opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, +2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software +Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the Internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the Internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +------ + +** Jemalloc; version 5.2.1 -- https://github.com/jemalloc/jemalloc +Copyright (C) 2002-present Jason Evans <jasone@canonware.com>. +All rights reserved. +Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved. +Copyright (C) 2009-present Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice(s), + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice(s), + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------ + +** The OpenGL Extension Wrangler Library; version 2.0.0 -- +http://glew.sourceforge.net/ +Copyright (C) 2008-2015, Nigel Stewart <nigels[]users sourceforge net> +Copyright (C) 2002-2008, Milan Ikits <milan ikits[]ieee org> +Copyright (C) 2002-2008, Marcelo E. Magallon <mmagallo[]debian org> +Copyright (C) 2002, Lev Povalahev +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* The name of the author may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + + +Mesa 3-D graphics library +Version: 7.0 + +Copyright (C) 1999-2007 Brian Paul All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Copyright (c) 2007 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
\ No newline at end of file diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 1191a172ac389e3d068a8ef7d16f36457e67e3b +Subproject a3fa40ec0ba525bc96cbfad49f854a0230b0524 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject eae381b698248e70e4a3c62bdf239f9d5a0470a +Subproject d71985e901986970dfc86c3d5d1124d0f8c2751 diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py index 40b76b617b3..fd4488ecd73 100644 --- a/release/scripts/modules/bl_i18n_utils/utils.py +++ b/release/scripts/modules/bl_i18n_utils/utils.py @@ -185,9 +185,9 @@ def list_po_dir(root_path, settings): """ Generator. List given directory (expecting one sub-directory per languages) and return all files matching languages listed in settings. - + Yield tuples (can_use, uid, num_id, name, isocode, po_path) - + Note that po_path may not actually exists. """ isocodes = ((e, os.path.join(root_path, e, e + ".po")) for e in os.listdir(root_path)) diff --git a/release/scripts/modules/bl_i18n_utils/utils_cli.py b/release/scripts/modules/bl_i18n_utils/utils_cli.py index d38911c122d..76dd8a740c5 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_cli.py +++ b/release/scripts/modules/bl_i18n_utils/utils_cli.py @@ -103,22 +103,22 @@ def main(): sub_parsers = parser.add_subparsers() sub_parser = sub_parsers.add_parser('update_po', help="Update a PO file from a given POT template file") - sub_parser.add_argument('--template', metavar='template.pot', required=True, + sub_parser.add_argument('--template', metavar='template.pot', required=True, help="The source pot file to use as template for the update.") sub_parser.add_argument('--dst', metavar='dst.po', required=True, help="The destination po to update.") - sub_parser.set_defaults(func=update_po) + sub_parser.set_defaults(func=update_po) sub_parser = sub_parsers.add_parser('cleanup_po', help="Cleanup a PO file (check for and fix some common errors, remove commented messages).") sub_parser.add_argument('--src', metavar='src.po', required=True, help="The source po file to clean up.") sub_parser.add_argument('--dst', metavar='dst.po', help="The destination po to write to.") - sub_parser.set_defaults(func=cleanup_po) + sub_parser.set_defaults(func=cleanup_po) sub_parser = sub_parsers.add_parser('strip_po', help="Reduce all non-essential data from given PO file (reduce its size).") sub_parser.add_argument('--src', metavar='src.po', required=True, help="The source po file to strip.") sub_parser.add_argument('--dst', metavar='dst.po', help="The destination po to write to.") - sub_parser.set_defaults(func=strip_po) + sub_parser.set_defaults(func=strip_po) sub_parser = sub_parsers.add_parser('rtl_process_po', help="Pre-process PO files for RTL languages.") @@ -128,7 +128,7 @@ def main(): sub_parser = sub_parsers.add_parser('language_menu', help="Generate the text file used by Blender to create its language menu.") - sub_parser.set_defaults(func=language_menu) + sub_parser.set_defaults(func=language_menu) args = parser.parse_args(sys.argv[1:]) diff --git a/release/scripts/modules/bl_keymap_utils/io.py b/release/scripts/modules/bl_keymap_utils/io.py index 091cdbc2642..645a145f994 100644 --- a/release/scripts/modules/bl_keymap_utils/io.py +++ b/release/scripts/modules/bl_keymap_utils/io.py @@ -222,12 +222,21 @@ def keyconfig_export_as_data(wm, kc, filepath, *, all_keymaps=False): fw("]\n") fw("\n\n") fw("if __name__ == \"__main__\":\n") + + # We could remove this in the future, as loading new key-maps in older Blender versions + # makes less and less sense as Blender changes. + fw(" # Only add keywords that are supported.\n") + fw(" from bpy.app import version as blender_version\n") + fw(" keywords = {}\n") + fw(" if blender_version >= (2, 92, 0):\n") + fw(" keywords[\"keyconfig_version\"] = keyconfig_version\n") + fw(" import os\n") fw(" from bl_keymap_utils.io import keyconfig_import_from_data\n") fw(" keyconfig_import_from_data(\n") fw(" os.path.splitext(os.path.basename(__file__))[0],\n") fw(" keyconfig_data,\n") - fw(" keyconfig_version=keyconfig_version,\n") + fw(" **keywords,\n") fw(" )\n") diff --git a/release/scripts/modules/bpy_extras/__init__.py b/release/scripts/modules/bpy_extras/__init__.py index 1caef074d43..cb990b014a1 100644 --- a/release/scripts/modules/bpy_extras/__init__.py +++ b/release/scripts/modules/bpy_extras/__init__.py @@ -24,6 +24,7 @@ Utility modules associated with the bpy module. __all__ = ( "anim_utils", + "asset_utils", "object_utils", "io_utils", "image_utils", diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py new file mode 100644 index 00000000000..db982e119d4 --- /dev/null +++ b/release/scripts/modules/bpy_extras/asset_utils.py @@ -0,0 +1,63 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +""" +Helpers for asset management tasks. +""" + +import bpy +from bpy.types import ( + Context, +) + +__all__ = ( + "SpaceAssetInfo", +) + +class SpaceAssetInfo: + @classmethod + def is_asset_browser(cls, space_data: bpy.types.Space): + return space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'ASSETS' + + @classmethod + def is_asset_browser_poll(cls, context: Context): + return cls.is_asset_browser(context.space_data) + + @classmethod + def get_active_asset(cls, context: Context): + if hasattr(context, "active_file"): + active_file = context.active_file + return active_file.asset_data if active_file else None + +class AssetBrowserPanel: + bl_space_type = 'FILE_BROWSER' + + @classmethod + def poll(cls, context): + return SpaceAssetInfo.is_asset_browser_poll(context) + +class AssetMetaDataPanel: + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + + @classmethod + def poll(cls, context): + active_file = context.active_file + return SpaceAssetInfo.is_asset_browser_poll(context) and active_file and active_file.asset_data diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 8171b9ce1a4..d3990851e2c 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -755,6 +755,7 @@ def km_property_editor(_params): # ShaderFX panels ("object.shaderfx_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}), ("object.shaderfx_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.shaderfx_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None), # Constraint panels ("constraint.delete", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}), ("constraint.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), @@ -1216,7 +1217,7 @@ def km_view3d(params): ("transform.mirror", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), ("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.use_snap')]}), - op_panel("VIEW3D_PT_snapping", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, [("keep_open", False)]), + op_panel("VIEW3D_PT_snapping", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, [("keep_open", True)]), ("object.transform_axis_target", {"type": 'T', "value": 'PRESS', "shift": True}, None), ("transform.skin_resize", {"type": 'A', "value": 'PRESS', "ctrl": True}, None), ]) @@ -2500,6 +2501,8 @@ def km_sequencer(params): ("sequencer.delete", {"type": 'DEL', "value": 'PRESS'}, None), ("sequencer.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), + ("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True, "shift": True}, + {"properties": [("keep_offset", True)]}), ("sequencer.images_separate", {"type": 'Y', "value": 'PRESS'}, None), ("sequencer.meta_toggle", {"type": 'TAB', "value": 'PRESS'}, None), ("sequencer.meta_make", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 54731c7139f..4b8a470a3ae 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -454,6 +454,17 @@ def km_property_editor(params): ("object.modifier_remove", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("report", True)]}), ("object.modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), ("object.modifier_copy", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), + # Grease pencil modifier panels + ("object.gpencil_modifier_remove", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.gpencil_modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.gpencil_modifier_copy", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), + # ShaderFX panels + ("object.shaderfx_remove", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.shaderfx_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("objectshaderfx_copy", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), + # Constraint panels + ("constraint.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("constraint.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), ]) return keymap diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index 71b2de41d9e..e91d3b3ce60 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -27,6 +27,7 @@ if "bpy" in locals(): _modules = [ "add_mesh_torus", "anim", + "assets", "clip", "console", "constraint", diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py index bb414b5ff89..e1d7f2057d2 100644 --- a/release/scripts/startup/bl_operators/anim.py +++ b/release/scripts/startup/bl_operators/anim.py @@ -307,7 +307,7 @@ class NLA_OT_bake(Operator): class ClearUselessActions(Operator): - """Mark actions with no F-Curves for deletion after save & reload of """ \ + """Mark actions with no F-Curves for deletion after save and reload of """ \ """file preserving \"action libraries\"""" bl_idname = "anim.clear_useless_actions" bl_label = "Clear Useless Actions" diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py new file mode 100644 index 00000000000..317555280e5 --- /dev/null +++ b/release/scripts/startup/bl_operators/assets.py @@ -0,0 +1,75 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import bpy + +from bpy_extras.asset_utils import ( + SpaceAssetInfo, +) + + +class ASSET_OT_tag_add(bpy.types.Operator): + """Add a new keyword tag to the active asset""" + + bl_idname = "asset.tag_add" + bl_label = "Add Asset Tag" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + return SpaceAssetInfo.is_asset_browser_poll(context) and SpaceAssetInfo.get_active_asset(context) + + def execute(self, context): + active_asset = SpaceAssetInfo.get_active_asset(context) + active_asset.tags.new("Unnamed Tag") + + return {'FINISHED'} + + +class ASSET_OT_tag_remove(bpy.types.Operator): + """Remove an existing keyword tag from the active asset""" + + bl_idname = "asset.tag_remove" + bl_label = "Remove Asset Tag" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + if not SpaceAssetInfo.is_asset_browser_poll(context): + return False + + active_asset = SpaceAssetInfo.get_active_asset(context) + if not active_asset: + return False + + return active_asset.active_tag in range(len(active_asset.tags)) + + def execute(self, context): + active_asset = SpaceAssetInfo.get_active_asset(context) + tag = active_asset.tags[active_asset.active_tag] + + active_asset.tags.remove(tag) + active_asset.active_tag -= 1 + + return {'FINISHED'} + + +classes = ( + ASSET_OT_tag_add, + ASSET_OT_tag_remove, +) diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py index 7d7e3793dba..b94104d10a9 100644 --- a/release/scripts/startup/bl_operators/geometry_nodes.py +++ b/release/scripts/startup/bl_operators/geometry_nodes.py @@ -18,6 +18,7 @@ import bpy + def geometry_node_group_empty_new(context): group = bpy.data.node_groups.new("Geometry Nodes", 'GeometryNodeTree') group.inputs.new('NodeSocketGeometry', "Geometry") @@ -33,6 +34,7 @@ def geometry_node_group_empty_new(context): return group + def geometry_modifier_poll(context) -> bool: ob = context.object @@ -42,6 +44,7 @@ def geometry_modifier_poll(context) -> bool: return True + class NewGeometryNodesModifier(bpy.types.Operator): """Create a new modifier with a new geometry node group""" diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index 87973ac2c45..92eb47cea0f 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -223,7 +223,7 @@ class SelectHierarchy(Operator): class SubdivisionSet(Operator): - """Sets a Subdivision Surface Level (1-5)""" + """Sets a Subdivision Surface level (1 to 5)""" bl_idname = "object.subdivision_set" bl_label = "Subdivision Set" @@ -888,7 +888,7 @@ class LoadImageAsEmpty: filter_folder: BoolProperty(default=True, options={'HIDDEN', 'SKIP_SAVE'}) view_align: BoolProperty( - name="Align to view", + name="Align to View", default=True, ) diff --git a/release/scripts/startup/bl_operators/object_align.py b/release/scripts/startup/bl_operators/object_align.py index ec9b098be2c..cc6724a620e 100644 --- a/release/scripts/startup/bl_operators/object_align.py +++ b/release/scripts/startup/bl_operators/object_align.py @@ -358,7 +358,7 @@ from bpy.props import ( class AlignObjects(Operator): - """Align Objects""" + """Align objects""" bl_idname = "object.align" bl_label = "Align Objects" bl_options = {'REGISTER', 'UNDO'} @@ -386,7 +386,7 @@ class AlignObjects(Operator): name="Relative To", description="Reference location to align to", items=( - ('OPT_1', "Scene Origin", "Use the Scene Origin as the position for the selected objects to align to"), + ('OPT_1', "Scene Origin", "Use the scene origin as the position for the selected objects to align to"), ('OPT_2', "3D Cursor", "Use the 3D cursor as the position for the selected objects to align to"), ('OPT_3', "Selection", "Use the selected objects as the position for the selected objects to align to"), ('OPT_4', "Active", "Use the active object as the position for the selected objects to align to"), diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py index 0f4eb8a8507..0600536cb66 100644 --- a/release/scripts/startup/bl_operators/object_quick_effects.py +++ b/release/scripts/startup/bl_operators/object_quick_effects.py @@ -134,7 +134,7 @@ class QuickExplode(ObjectModeOperator, Operator): default='EXPLODE', ) amount: IntProperty( - name="Amount of pieces", + name="Number of Pieces", min=2, max=10000, soft_min=2, soft_max=10000, default=100, @@ -337,7 +337,7 @@ class QuickSmoke(ObjectModeOperator, Operator): items=( ('SMOKE', "Smoke", ""), ('FIRE', "Fire", ""), - ('BOTH', "Smoke + Fire", ""), + ('BOTH', "Smoke & Fire", ""), ), default='SMOKE', ) @@ -573,6 +573,7 @@ class QuickLiquid(Operator): return {'FINISHED'} + classes = ( QuickExplode, QuickFur, diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 8201ce080b1..c663a736441 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -893,6 +893,7 @@ class GreasePencilLayerDisplayPanel: row = layout.row(align=True) row.prop(gpl, "use_solo_mode", text="Show Only on Keyframed") + class GreasePencilFlipTintColors(Operator): bl_label = "Flip Colors" bl_idname = "gpencil.tint_flip" diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index 6aaec9940e8..47ab98386f4 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -42,6 +42,9 @@ class MATERIAL_UL_matslots(UIList): # ob = data slot = item ma = slot.material + + layout.context_pointer_set("id", ma) + if self.layout_type in {'DEFAULT', 'COMPACT'}: if ma: layout.prop(ma, "name", text="", emboss=False, icon_value=icon) diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 1ae1826b609..5d241e8e216 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -550,7 +550,6 @@ def brush_settings(layout, context, brush, popover=False): if context.preferences.experimental.use_sculpt_tools_tilt and capabilities.has_tilt: layout.prop(brush, "tilt_strength_factor", slider=True) - row = layout.row(align=True) row.prop(brush, "hardness", slider=True) row.prop(brush, "invert_hardness_pressure", text="") @@ -765,6 +764,10 @@ def brush_settings(layout, context, brush, popover=False): col.prop(brush, "surface_smooth_current_vertex") col.prop(brush, "surface_smooth_iterations") + elif sculpt_tool == 'DISPLACEMENT_SMEAR': + col = layout.column() + col.prop(brush, "smear_deform_type") + elif sculpt_tool == 'MASK': layout.row().prop(brush, "mask_tool", expand=True) @@ -1194,6 +1197,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row.prop(brush, "size", text="Radius") row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY') + row.prop(gp_settings, "use_default_eraser", text="") row = layout.row(align=True) row.prop(gp_settings, "eraser_mode", expand=True) diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py index 27df3b10853..b5e6942f19d 100644 --- a/release/scripts/startup/bl_ui/properties_view_layer.py +++ b/release/scripts/startup/bl_ui/properties_view_layer.py @@ -113,8 +113,7 @@ class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel): col.prop(view_layer, "use_pass_glossy_color", text="Color") col = layout.column(heading="Volume", align=True) - col.prop(view_layer_eevee, "use_pass_volume_transmittance", text="Transmittance") - col.prop(view_layer_eevee, "use_pass_volume_scatter", text="Scatter") + col.prop(view_layer_eevee, "use_pass_volume_direct", text="Light") col = layout.column(heading="Other", align=True) col.prop(view_layer, "use_pass_emit", text="Emission") @@ -187,8 +186,8 @@ class ViewLayerCryptomattePanel(ViewLayerButtonsPanel, Panel): col.prop(view_layer, "use_pass_cryptomatte_material", text="Material") col.prop(view_layer, "use_pass_cryptomatte_asset", text="Asset") col = layout.column() - col.active = any((view_layer.use_pass_cryptomatte_object, - view_layer.use_pass_cryptomatte_material, + col.active = any((view_layer.use_pass_cryptomatte_object, + view_layer.use_pass_cryptomatte_material, view_layer.use_pass_cryptomatte_asset)) col.prop(view_layer, "pass_cryptomatte_depth", text="Levels") col.prop(view_layer, "use_pass_cryptomatte_accurate", text="Accurate Mode") diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index a9bb2e79762..98b155d8a21 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -17,27 +17,79 @@ # ##### END GPL LICENSE BLOCK ##### # <pep8 compliant> + +import bpy + from bpy.types import Header, Panel, Menu, UIList +from bpy_extras import ( + asset_utils, +) + class FILEBROWSER_HT_header(Header): bl_space_type = 'FILE_BROWSER' + def draw_asset_browser_buttons(self, context): + layout = self.layout + + space_data = context.space_data + params = space_data.params + + row = layout.row(align=True) + row.prop(params, "asset_library", text="") + # External libraries don't auto-refresh, add refresh button. + if params.asset_library != 'LOCAL': + row.operator("file.refresh", text="", icon="FILE_REFRESH") + + layout.separator_spacer() + + # Uses prop_with_popover() as popover() only adds the triangle icon in headers. + layout.prop_with_popover( + params, + "display_type", + panel="FILEBROWSER_PT_display", + text="", + icon_only=True, + ) + layout.prop_with_popover( + params, + "display_type", + panel="FILEBROWSER_PT_filter", + text="", + icon='FILTER', + icon_only=True, + ) + + layout.prop(params, "filter_search", text="", icon='VIEWZOOM') + + layout.operator( + "screen.region_toggle", + text="", + icon='PREFERENCES', + depress=is_option_region_visible(context, space_data) + ).region_type = 'TOOL_PROPS' + def draw(self, context): + from bpy_extras.asset_utils import SpaceAssetInfo + layout = self.layout - st = context.space_data + space_data = context.space_data - if st.active_operator is None: + if space_data.active_operator is None: layout.template_header() FILEBROWSER_MT_editor_menus.draw_collapsible(context, layout) - # can be None when save/reload with a file selector open - - layout.separator_spacer() + if SpaceAssetInfo.is_asset_browser(space_data): + layout.separator() + self.draw_asset_browser_buttons(context) + else: + layout.separator_spacer() - layout.template_running_jobs() + if not context.screen.show_statusbar: + layout.template_running_jobs() class FILEBROWSER_PT_display(Panel): @@ -144,6 +196,9 @@ class FILEBROWSER_PT_filter(Panel): row.label(icon='BLANK1') # Indentation sub = row.column(align=True) + + sub.prop(params, "use_filter_asset_only") + filter_id = params.filter_id for identifier in dir(filter_id): if identifier.startswith("category_"): @@ -160,6 +215,11 @@ def panel_poll_is_upper_region(region): return region.alignment in {'LEFT', 'RIGHT'} +def panel_poll_is_asset_browsing(context): + from bpy_extras.asset_utils import SpaceAssetInfo + return SpaceAssetInfo.is_asset_browser_poll(context) + + class FILEBROWSER_UL_dir(UIList): def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): direntry = item @@ -187,7 +247,7 @@ class FILEBROWSER_PT_bookmarks_volumes(Panel): @classmethod def poll(cls, context): - return panel_poll_is_upper_region(context.region) + return panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) def draw(self, context): layout = self.layout @@ -207,7 +267,11 @@ class FILEBROWSER_PT_bookmarks_system(Panel): @classmethod def poll(cls, context): - return not context.preferences.filepaths.hide_system_bookmarks and panel_poll_is_upper_region(context.region) + return ( + not context.preferences.filepaths.hide_system_bookmarks and + panel_poll_is_upper_region(context.region) and + not panel_poll_is_asset_browsing(context) + ) def draw(self, context): layout = self.layout @@ -241,7 +305,10 @@ class FILEBROWSER_PT_bookmarks_favorites(Panel): @classmethod def poll(cls, context): - return panel_poll_is_upper_region(context.region) + return ( + panel_poll_is_upper_region(context.region) and + not panel_poll_is_asset_browsing(context) + ) def draw(self, context): layout = self.layout @@ -278,7 +345,11 @@ class FILEBROWSER_PT_bookmarks_recents(Panel): @classmethod def poll(cls, context): - return not context.preferences.filepaths.hide_recent_locations and panel_poll_is_upper_region(context.region) + return ( + not context.preferences.filepaths.hide_recent_locations and + panel_poll_is_upper_region(context.region) and + not panel_poll_is_asset_browsing(context) + ) def draw(self, context): layout = self.layout @@ -302,7 +373,11 @@ class FILEBROWSER_PT_advanced_filter(Panel): @classmethod def poll(cls, context): # only useful in append/link (library) context currently... - return context.space_data.params.use_library_browsing and panel_poll_is_upper_region(context.region) + return ( + context.space_data.params.use_library_browsing and + panel_poll_is_upper_region(context.region) and + not panel_poll_is_asset_browsing(context) + ) def draw(self, context): layout = self.layout @@ -314,12 +389,26 @@ class FILEBROWSER_PT_advanced_filter(Panel): if params.use_filter_blendid: layout.separator() col = layout.column(align=True) + + col.prop(params, "use_filter_asset_only") + filter_id = params.filter_id for identifier in dir(filter_id): if identifier.startswith("filter_"): col.prop(filter_id, identifier, toggle=True) +def is_option_region_visible(context, space): + if not space.active_operator: + return False + + for region in context.area.regions: + if region.type == 'TOOL_PROPS' and region.width <= 1: + return False + + return True + + class FILEBROWSER_PT_directory_path(Panel): bl_space_type = 'FILE_BROWSER' bl_region_type = 'UI' @@ -334,16 +423,6 @@ class FILEBROWSER_PT_directory_path(Panel): return True - def is_option_region_visible(self, context, space): - if not space.active_operator: - return False - - for region in context.area.regions: - if region.type == 'TOOL_PROPS' and region.width <= 1: - return False - - return True - def draw(self, context): layout = self.layout space = context.space_data @@ -388,7 +467,7 @@ class FILEBROWSER_PT_directory_path(Panel): "screen.region_toggle", text="", icon='PREFERENCES', - depress=self.is_option_region_visible(context, space) + depress=is_option_region_visible(context, space) ).region_type = 'TOOL_PROPS' @@ -482,6 +561,99 @@ class FILEBROWSER_MT_context_menu(Menu): layout.prop_menu_enum(params, "sort_method") +class ASSETBROWSER_PT_navigation_bar(asset_utils.AssetBrowserPanel, Panel): + bl_label = "Asset Navigation" + bl_region_type = 'TOOLS' + bl_options = {'HIDE_HEADER'} + + def draw(self, context): + layout = self.layout + + space_file = context.space_data + + col = layout.column() + + col.scale_x = 1.3 + col.scale_y = 1.3 + col.prop(space_file.params, "asset_category", expand=True) + + +class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): + bl_region_type = 'TOOL_PROPS' + bl_label = "Asset Metadata" + bl_options = {'HIDE_HEADER'} + + def draw(self, context): + layout = self.layout + active_file = context.active_file + active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context) + + if not active_file or not active_asset: + layout.label(text="No asset selected.", icon='INFO') + return + + # If the active file is an ID, use its name directly so renaming is possible from right here. + layout.prop(context.id if context.id is not None else active_file, "name", text="") + + +class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel): + bl_label = "Preview" + + def draw(self, context): + layout = self.layout + active_file = context.active_file + + row = layout.row() + box = row.box() + box.template_icon(icon_value=active_file.preview_icon_id, scale=5.0) + if bpy.ops.ed.lib_id_load_custom_preview.poll(): + col = row.column(align=True) + col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="") + col.separator() + col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="") + + +class ASSETBROWSER_PT_metadata_details(asset_utils.AssetMetaDataPanel, Panel): + bl_label = "Details" + + def draw(self, context): + layout = self.layout + active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context) + + layout.use_property_split = True + + if active_asset: + layout.prop(active_asset, "description") + + +class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel): + bl_label = "Tags" + + def draw(self, context): + layout = self.layout + asset_data = asset_utils.SpaceAssetInfo.get_active_asset(context) + + row = layout.row() + row.template_list("ASSETBROWSER_UL_metadata_tags", "asset_tags", asset_data, "tags", + asset_data, "active_tag", rows=4) + + col = row.column(align=True) + col.operator("asset.tag_add", icon='ADD', text="") + col.operator("asset.tag_remove", icon='REMOVE', text="") + + +class ASSETBROWSER_UL_metadata_tags(UIList): + def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): + tag = item + + row = layout.row(align=True) + # Non-editable entries would show grayed-out, which is bad in this specific case, so switch to mere label. + if tag.is_property_readonly("name"): + row.label(text=tag.name, icon_value=icon) + else: + row.prop(tag, "name", text="", emboss=False, icon_value=icon) + + classes = ( FILEBROWSER_HT_header, FILEBROWSER_PT_display, @@ -498,6 +670,12 @@ classes = ( FILEBROWSER_MT_view, FILEBROWSER_MT_select, FILEBROWSER_MT_context_menu, + ASSETBROWSER_PT_navigation_bar, + ASSETBROWSER_PT_metadata, + ASSETBROWSER_PT_metadata_preview, + ASSETBROWSER_PT_metadata_details, + ASSETBROWSER_PT_metadata_tags, + ASSETBROWSER_UL_metadata_tags, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index f4e88b70281..342b72acb8c 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -1465,7 +1465,7 @@ class IMAGE_PT_overlay(Panel): bl_ui_units_x = 13 def draw(self, context): - pass + pass class IMAGE_PT_overlay_uv_edit(Panel): @@ -1496,7 +1496,6 @@ class IMAGE_PT_overlay_uv_edit(Panel): subrow.prop(uvedit, "display_stretch_type", text="") - class IMAGE_PT_overlay_uv_edit_geometry(Panel): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'HEADER' @@ -1529,7 +1528,6 @@ class IMAGE_PT_overlay_uv_edit_geometry(Panel): row.prop(uvedit, "show_faces", text="Faces") - class IMAGE_PT_overlay_texture_paint(Panel): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'HEADER' diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 1c4d1919f43..e6af83b61f4 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -168,7 +168,6 @@ class NODE_HT_header(Header): else: row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier") - else: # Custom node tree is edited as independent ID block NODE_MT_editor_menus.draw_collapsible(context, layout) diff --git a/release/scripts/startup/bl_ui/space_properties.py b/release/scripts/startup/bl_ui/space_properties.py index 0f64ab63d6b..765cab1ace2 100644 --- a/release/scripts/startup/bl_ui/space_properties.py +++ b/release/scripts/startup/bl_ui/space_properties.py @@ -61,8 +61,10 @@ class PROPERTIES_PT_navigation_bar(Panel): layout.scale_x = 1.4 layout.scale_y = 1.4 if view.search_filter: - layout.prop_tabs_enum(view, "context", data_highlight=view, - property_highlight="tab_search_results", icon_only=True) + layout.prop_tabs_enum( + view, "context", data_highlight=view, + property_highlight="tab_search_results", icon_only=True, + ) else: layout.prop_tabs_enum(view, "context", icon_only=True) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 3f92fce81f6..e79b5d3e2c8 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -129,6 +129,8 @@ class SEQUENCER_HT_header(Header): layout = self.layout st = context.space_data + scene = context.scene + sequencer_tool_settings = context.tool_settings.sequencer_tool_settings show_region_tool_header = st.show_region_tool_header @@ -139,12 +141,17 @@ class SEQUENCER_HT_header(Header): SEQUENCER_MT_editor_menus.draw_collapsible(context, layout) - if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}: - + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: + layout.separator_spacer() + row = layout.row(align=True) + row.prop(sequencer_tool_settings, "fit_method", text="") layout.separator_spacer() - layout.prop(st, "display_mode", text="", icon_only=True) + if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}: + if st.view_type == 'PREVIEW': + layout.separator_spacer() + layout.prop(st, "display_mode", text="", icon_only=True) layout.prop(st, "preview_channels", text="", icon_only=True) gpd = context.gpencil_data @@ -157,6 +164,12 @@ class SEQUENCER_HT_header(Header): if tool_settings.use_proportional_edit: row.prop(tool_settings, "proportional_edit_falloff", icon_only=True) + row = layout.row(align=True) + row.prop(st, "show_strip_overlay", text="", icon='OVERLAY') + sub = row.row(align=True) + sub.popover(panel="SEQUENCER_PT_overlay", text="") + sub.active = st.show_strip_overlay + class SEQUENCER_MT_editor_menus(Menu): bl_idname = "SEQUENCER_MT_editor_menus" @@ -176,6 +189,80 @@ class SEQUENCER_MT_editor_menus(Menu): layout.menu("SEQUENCER_MT_strip") +class SEQUENCER_PT_overlay(Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'HEADER' + bl_label = "Overlays" + bl_ui_units_x = 7 + + def draw(self, _context): + pass + + +class SEQUENCER_PT_overlay(Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'HEADER' + bl_label = "Overlays" + bl_ui_units_x = 7 + + def draw(self, _context): + pass + + +class SEQUENCER_PT_preview_overlay(Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'HEADER' + bl_parent_id = 'SEQUENCER_PT_overlay' + bl_label = "Preview Overlays" + + @classmethod + def poll(cls, context): + st = context.space_data + return st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'} and st.display_mode == 'IMAGE' + + def draw(self, context): + ed = context.scene.sequence_editor + st = context.space_data + layout = self.layout + + layout.active = st.show_strip_overlay + layout.prop(ed, "show_overlay", text="Frame Overlay") + layout.prop(st, "show_safe_areas", text="Safe Areas") + layout.prop(st, "show_metadata", text="Metadata") + layout.prop(st, "show_annotation", text="Annotations") + + +class SEQUENCER_PT_sequencer_overlay(Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'HEADER' + bl_parent_id = 'SEQUENCER_PT_overlay' + bl_label = "Sequencer Overlays" + + @classmethod + def poll(cls, context): + st = context.space_data + return st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'} + + def draw(self, context): + st = context.space_data + layout = self.layout + + layout.active = st.show_strip_overlay + + layout.prop(st, "show_strip_name", text="Name") + layout.prop(st, "show_strip_source", text="Source") + layout.prop(st, "show_strip_duration", text="Duration") + + layout.separator() + + layout.prop(st, "show_strip_offset", text="Offsets") + layout.prop(st, "show_fcurves", text="F-Curves") + + layout.separator() + + layout.prop_menu_enum(st, "waveform_display_type") + + class SEQUENCER_MT_view_cache(Menu): bl_label = "Cache" @@ -294,6 +381,12 @@ class SEQUENCER_MT_view(Menu): layout.operator("view2d.zoom_border", text="Zoom") layout.menu("SEQUENCER_MT_preview_zoom") + if st.display_mode == 'IMAGE': + layout.prop(st, "use_zoom_to_fit") + elif st.display_mode == 'WAVEFORM': + layout.separator() + layout.prop(st, "show_separate_color", text="Show Separate Color Channels") + layout.separator() layout.menu("SEQUENCER_MT_proxy") @@ -318,22 +411,8 @@ class SEQUENCER_MT_view(Menu): layout.separator() layout.prop(st, "show_seconds") - layout.prop(st, "show_strip_offset") - layout.prop(st, "show_fcurves") layout.prop(st, "show_markers") layout.menu("SEQUENCER_MT_view_cache", text="Show Cache") - layout.prop_menu_enum(st, "waveform_display_type", text="Show Waveforms") - - if is_preview: - layout.separator() - if st.display_mode == 'IMAGE': - layout.prop(st, "use_zoom_to_fit") - layout.prop(ed, "show_overlay", text="Show Frame Overlay") - layout.prop(st, "show_safe_areas", text="Show Safe Areas") - layout.prop(st, "show_metadata", text="Show Metadata") - layout.prop(st, "show_annotation", text="Show Annotations") - elif st.display_mode == 'WAVEFORM': - layout.prop(st, "show_separate_color", text="Show Separate Color Channels") layout.separator() @@ -629,6 +708,22 @@ class SEQUENCER_MT_add_effect(Menu): col.enabled = selected_sequences_len(context) != 0 +class SEQUENCER_MT_strip_image_transform(Menu): + bl_label = "Image Transform" + + def draw(self, _context): + layout = self.layout + + layout.operator("sequencer.strip_transform_fit", text="Scale To Fit").fit_method = 'FIT' + layout.operator("sequencer.strip_transform_fit", text="Scale to Fill").fit_method = 'FILL' + layout.operator("sequencer.strip_transform_fit", text="Stretch To Fill").fit_method = 'STRETCH' + layout.separator() + + layout.operator("sequencer.strip_transform_clear", text="Clear Position").property = 'POSITION' + layout.operator("sequencer.strip_transform_clear", text="Clear Scale").property = 'SCALE' + layout.operator("sequencer.strip_transform_clear", text="Clear Rotation").property = 'ROTATION' + layout.operator("sequencer.strip_transform_clear", text="Clear All").property = 'ALL' + class SEQUENCER_MT_strip_transform(Menu): bl_label = "Transform" @@ -723,6 +818,7 @@ class SEQUENCER_MT_strip(Menu): layout.separator() layout.menu("SEQUENCER_MT_strip_transform") + layout.menu("SEQUENCER_MT_strip_image_transform") layout.separator() layout.operator("sequencer.split", text="Split").type = 'SOFT' @@ -2214,6 +2310,7 @@ classes = ( SEQUENCER_MT_strip_effect, SEQUENCER_MT_strip_movie, SEQUENCER_MT_strip, + SEQUENCER_MT_strip_image_transform, SEQUENCER_MT_strip_transform, SEQUENCER_MT_strip_input, SEQUENCER_MT_strip_lock_mute, @@ -2222,6 +2319,10 @@ classes = ( SEQUENCER_PT_active_tool, SEQUENCER_PT_strip, + SEQUENCER_PT_overlay, + SEQUENCER_PT_preview_overlay, + SEQUENCER_PT_sequencer_overlay, + SEQUENCER_PT_effect, SEQUENCER_PT_scene, SEQUENCER_PT_mask, diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index 16b02db9377..3b9ce5311f3 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -297,6 +297,7 @@ class TIME_PT_keyframing_settings(TimelinePanelButtons, Panel): col.label(text="New Keyframe Type") col.prop(tool_settings, "keyframe_type", text="") + class TIME_PT_auto_keyframing(TimelinePanelButtons, Panel): bl_label = "Auto Keyframing" bl_options = {'HIDE_HEADER'} diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index ca5e6404159..0411d5c64bc 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -360,7 +360,7 @@ class _defs_transform: idname="builtin.transform", label="Transform", description=( - "Supports any combination of grab, rotate & scale at once" + "Supports any combination of grab, rotate, and scale at once" ), icon="ops.transform.transform", widget="VIEW3D_GGT_xform_gizmo", @@ -1654,7 +1654,7 @@ class _defs_image_uv_transform: idname="builtin.transform", label="Transform", description=( - "Supports any combination of grab, rotate & scale at once" + "Supports any combination of grab, rotate, and scale at once" ), icon="ops.transform.transform", widget="IMAGE_GGT_gizmo2d", @@ -2903,10 +2903,10 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_sequencer_generic.blade, ], 'SEQUENCER_PREVIEW': [ - _defs_sequencer_generic.sample, *_tools_select, - *_tools_annotate, _defs_sequencer_generic.blade, + _defs_sequencer_generic.sample, + *_tools_annotate, ], } diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 761e60aef30..0167f0b1e20 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -207,11 +207,11 @@ class USERPREF_PT_interface_display(InterfacePanel, CenterAlignMixIn, Panel): col.prop(view, "ui_line_width", text="Line Width") col.prop(view, "show_splash", text="Splash Screen") col.prop(view, "show_developer_ui") - + col.separator() col = layout.column(heading="Tooltips", align=True) - col.prop(view, "show_tooltips", text = "User Tooltips") + col.prop(view, "show_tooltips", text="User Tooltips") sub = col.column() sub.active = view.show_tooltips sub.prop(view, "show_tooltips_python") @@ -1339,6 +1339,40 @@ class USERPREF_PT_saveload_autorun(FilePathsPanel, Panel): row.operator("preferences.autoexec_path_remove", text="", icon='X', emboss=False).index = i +class USERPREF_PT_file_paths_asset_libraries(FilePathsPanel, Panel): + bl_label = "Asset Libraries" + + def draw(self, context): + layout = self.layout + layout.use_property_split = False + layout.use_property_decorate = False + + paths = context.preferences.filepaths + + box = layout.box() + split = box.split(factor=0.35) + name_col = split.column() + path_col = split.column() + + row = name_col.row(align=True) # Padding + row.separator() + row.label(text="Name") + + row = path_col.row(align=True) # Padding + row.separator() + row.label(text="Path") + + + for i, library in enumerate(paths.asset_libraries): + name_col.prop(library, "name", text="") + row = path_col.row() + row.prop(library, "path", text="") + row.operator("preferences.asset_library_remove", text="", icon='X', emboss=False).index = i + row = box.row() + row.alignment = 'LEFT' + row.operator("preferences.asset_library_add", text="", icon='ADD', emboss=False) + + # ----------------------------------------------------------------------------- # Save/Load Panels @@ -1543,8 +1577,6 @@ class USERPREF_PT_navigation_zoom(NavigationPanel, CenterAlignMixIn, Panel): col.prop(inputs, "use_zoom_to_mouse") col.prop(inputs, "invert_zoom_wheel", text="Invert Wheel Zoom Direction") - # sub.prop(view, "wheel_scroll_lines", text="Scroll Lines") - class USERPREF_PT_navigation_fly_walk(NavigationPanel, CenterAlignMixIn, Panel): bl_label = "Fly & Walk" @@ -1850,12 +1882,12 @@ class USERPREF_PT_addons(AddOnPanel, Panel): is_visible = is_visible and is_enabled if is_visible: - if search and search not in info["name"].lower(): - if info["author"]: - if search not in info["author"].lower(): - continue - else: - continue + if search and not ( + (search in info["name"].lower()) or + (info["author"] and (search in info["author"].lower())) or + ((filter == "All") and (search in info["category"].lower())) + ): + continue # Skip 2.7x add-ons included with Blender, unless in debug mode. is_addon_27x = info.get("blender", (0,)) < (2, 80) @@ -2204,6 +2236,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): self._draw_items( context, ( ({"property": "use_new_hair_type"}, "T68981"), + ({"property": "use_new_point_cloud_type"}, "T75717"), ), ) @@ -2289,6 +2322,7 @@ classes = ( USERPREF_PT_file_paths_render, USERPREF_PT_file_paths_applications, USERPREF_PT_file_paths_development, + USERPREF_PT_file_paths_asset_libraries, USERPREF_PT_saveload_blend, USERPREF_PT_saveload_blend_autosave, diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 10c9f25b92a..8b5183faf62 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2147,7 +2147,8 @@ class VIEW3D_MT_add(Menu): layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT') if context.preferences.experimental.use_new_hair_type: layout.operator("object.hair_add", text="Hair", icon='OUTLINER_OB_HAIR') - layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD') + if context.preferences.experimental.use_new_point_cloud_type: + layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD') layout.menu("VIEW3D_MT_volume_add", text="Volume", icon='OUTLINER_OB_VOLUME') layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL') @@ -5305,6 +5306,7 @@ class VIEW3D_MT_sculpt_mask_edit_pie(Menu): op.filter_type = 'CONTRAST_DECREASE' op.auto_iteration_count = False + class VIEW3D_MT_sculpt_automasking_pie(Menu): bl_label = "Automasking" diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 293d55a6015..577f9678a62 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -966,6 +966,7 @@ class VIEW3D_PT_tools_weightpaint_symmetry(Panel, View3DPaintPanel): row.active = mesh.use_mirror_vertex_group_x row.prop(mesh, "use_mirror_topology") + class VIEW3D_PT_tools_weightpaint_symmetry_for_topbar(Panel): bl_space_type = 'TOPBAR' bl_region_type = 'HEADER' diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index abab50b95a2..c3ab1b3db97 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -481,8 +481,11 @@ def not_implemented_node(idname): geometry_node_categories = [ # Geometry Nodes GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ - NodeItem("GeometryNodeRandomAttribute"), + NodeItem("GeometryNodeAttributeRandomize"), NodeItem("GeometryNodeAttributeMath"), + NodeItem("GeometryNodeAttributeFill"), + NodeItem("GeometryNodeAttributeMix"), + NodeItem("GeometryNodeAttributeColorRamp"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ NodeItem("ShaderNodeValToRGB"), diff --git a/release/steam/README.md b/release/steam/README.md new file mode 100644 index 00000000000..05eda799c3f --- /dev/null +++ b/release/steam/README.md @@ -0,0 +1,70 @@ +Creating Steam builds for Blender +================================= + +This script automates creation of the Steam files: download of the archives, +extraction of the archives, preparation of the build scripts (VDF files), actual +building of the Steam game files. + +Requirements +============ + +* MacOS machine - Tested on Catalina 10.15.6. Extracting contents from the DMG + archive did not work Windows nor on Linux using 7-zip. All DMG archives tested + failed to be extracted. As such only MacOS is known to work. +* Steam SDK downloaded from SteamWorks - The `steamcmd` is used to generate the + Steam game files. The path to the `steamcmd` is what is actually needed. +* SteamWorks credentials - Needed to log in using `steamcmd`. +* Login to SteamWorks with the `steamcmd` from the command-line at least once - + Needded to ensure the user is properly logged in. On a new machine the user + will have to go through two-factor authentication. +* App ID and Depot IDs - Needed to create the VDF files. +* Python 3.x - 3.7 was tested. +* Base URL - for downloading the archives. + +Usage +===== + +```bash +$ export STEAMUSER=SteamUserName +$ export STEAMPW=SteamUserPW +$ export BASEURL=https://download.blender.org/release/Blender2.83/ +$ export VERSION=2.83.3 +$ export APPID=appidnr +$ export WINID=winidnr +$ export LINID=linuxidnr +$ export MACOSID=macosidnr + +# log in to SteamWorks from command-line at least once + +$ ../sdk/tools/ContentBuilder/builder_osx/steamcmd +login $STEAMUSER $STEAMPW + +# once that has been done we can now actually start our tool + +$ python3.7 create_steam_builds.py --baseurl $BASEURL --version $VERSION --appid $APPID --winid $WINID --linuxid $LINID --macosid $MACOSID --steamuser $STEAMUSER --steampw $STEAMPW --steamcmd ../sdk/tools/ContentBuilder/builder_osx/steamcmd +``` + +All arguments in the above example are required. + +At the start the tool will login using `steamcmd`. This is necessary to let the +Steam SDK update itself if necessary. + +There are a few optional arguments: + +* `--dryrun`: If set building the game files will not actually happen. A set of + log files and a preview manifest per depot will be created in the output folder. + This can be used to double-check everything works as expected. +* `--skipdl`: If set will skip downloading of the archives. The tool expects the + archives to already exist in the correct content location. +* `--skipextract`: If set will skip extraction of all archives. The tool expects + the archives to already have been correctly extracted in the content location. + +Run the tool with `-h` for detailed information on each argument. + +The content and output folders are generated through appending the version +without dots to the words `content` and `output` respectively, e.g. `content2833` +and `output2833`. These folders are created next to the tool. + +From all `.template` files the Steam build scripts will be generated also in the +same directory as the tool. The files will have the extension `.vdf`. + +In case of errors the tool will have a non-zero return code.
\ No newline at end of file diff --git a/release/steam/blender_app_build.vdf.template b/release/steam/blender_app_build.vdf.template new file mode 100644 index 00000000000..9e2d0625d72 --- /dev/null +++ b/release/steam/blender_app_build.vdf.template @@ -0,0 +1,17 @@ +"appbuild" +{ + "appid" "[APPID]" + "desc" "Blender [VERSION]" // description for this build + "buildoutput" "./[OUTPUT]" // build output folder for .log, .csm & .csd files, relative to location of this file + "contentroot" "./[CONTENT]" // root content folder, relative to location of this file + "setlive" "" // branch to set live after successful build, non if empty + "preview" "[DRYRUN]" // 1 to enable preview builds, 0 to commit build to steampipe + "local" "" // set to flie path of local content server + + "depots" + { + "[WINID]" "depot_build_win.vdf" + "[LINUXID]" "depot_build_linux.vdf" + "[MACOSID]" "depot_build_macos.vdf" + } +} diff --git a/release/steam/create_steam_builds.py b/release/steam/create_steam_builds.py new file mode 100644 index 00000000000..2ecd0c347f7 --- /dev/null +++ b/release/steam/create_steam_builds.py @@ -0,0 +1,397 @@ +#!/usr/bin/env python3 + +import argparse +import pathlib +import requests +import shutil +import subprocess +from typing import Callable, Iterator, List, Tuple + +# supported archive and platform endings, used to create actual archive names +archive_endings = ["windows64.zip", "linux64.tar.xz", "macOS.dmg"] + + +def add_optional_argument(option: str, help: str) -> None: + global parser + """Add an optional argument + + Args: + option (str): Option to add + help (str): Help description for the argument + """ + parser.add_argument(option, help=help, action='store_const', const=1) + + +def blender_archives(version: str) -> Iterator[str]: + """Generator for Blender archives for version. + + Yields for items in archive_endings an archive name in the form of + blender-{version}-{ending}. + + Args: + version (str): Version string of the form 2.83.2 + + + Yields: + Iterator[str]: Name in the form of blender-{version}-{ending} + """ + global archive_endings + + for ending in archive_endings: + yield f"blender-{version}-{ending}" + + +def get_archive_type(archive_type: str, version: str) -> str: + """Return the archive of given type and version. + + Args: + archive_type (str): extension for archive type to check for + version (str): Version string in the form 2.83.2 + + Raises: + Exception: Execption when archive type isn't found + + Returns: + str: archive name for given type + """ + + for archive in blender_archives(version): + if archive.endswith(archive_type): + return archive + raise Exception("Unknown archive type") + + +def execute_command(cmd: List[str], name: str, errcode: int, cwd=".", capture_output=True) -> str: + """Execute the given command. + + Returns the process stdout upon success if any. + + On error print message the command with name that has failed. Print stdout + and stderr of the process if any, and then exit with given error code. + + Args: + cmd (List[str]): Command in list format, each argument as their own item + name (str): Name of command to use when printing to command-line + errcode (int): Error code to use in case of exit() + cwd (str, optional): Folder to use as current work directory for command + execution. Defaults to ".". + capture_output (bool, optional): Whether to capture command output or not. + Defaults to True. + + Returns: + str: stdout if any, or empty string + """ + cmd_process = subprocess.run( + cmd, capture_output=capture_output, encoding="UTF-8", cwd=cwd) + if cmd_process.returncode == 0: + if cmd_process.stdout: + return cmd_process.stdout + else: + return "" + else: + print(f"ERROR: {name} failed.") + if cmd_process.stdout: + print(cmd_process.stdout) + if cmd_process.stderr: + print(cmd_process.stderr) + exit(errcode) + return "" + + +def download_archives(base_url: str, archives: Callable[[str], Iterator[str]], version: str, dst_dir: pathlib.Path): + """Download archives from the given base_url. + + Archives is a generator for Blender archive names based on version. + + Archive names are appended to the base_url to load from, and appended to + dst_dir to save to. + + Args: + base_url (str): Base URL to load archives from + archives (Callable[[str], Iterator[str]]): Generator for Blender archive + names based on version + version (str): Version string in the form of 2.83.2 + dst_dir (pathlib.Path): Download destination + """ + + if base_url[-1] != '/': + base_url = base_url + '/' + + for archive in archives(version): + download_url = f"{base_url}{archive}" + target_file = dst_dir.joinpath(archive) + download_file(download_url, target_file) + + +def download_file(from_url: str, to_file: pathlib.Path) -> None: + """Download from_url as to_file. + + Actual downloading will be skipped if --skipdl is given on the command-line. + + Args: + from_url (str): Full URL to resource to download + to_file (pathlib.Path): Full path to save downloaded resource as + """ + global args + + if not args.skipdl or not to_file.exists(): + print(f"Downloading {from_url}") + with open(to_file, "wb") as download_zip: + response = requests.get(from_url) + if response.status_code != requests.codes.ok: + print(f"ERROR: failed to download {from_url} (status code: {response.status_code})") + exit(1313) + download_zip.write(response.content) + else: + print(f"Downloading {from_url} skipped") + print(" ... OK") + + +def copy_contents_from_dmg_to_path(dmg_file: pathlib.Path, dst: pathlib.Path) -> None: + """Copy the contents of the given DMG file to the destination folder. + + Args: + dmg_file (pathlib.Path): Full path to DMG archive to extract from + dst (pathlib.Path): Full path to destination to extract to + """ + hdiutil_attach = ["hdiutil", + "attach", + "-readonly", + f"{dmg_file}" + ] + attached = execute_command(hdiutil_attach, "hdiutil attach", 1) + + # Last line of output is what we want, it is of the form + # /dev/somedisk Apple_HFS /Volumes/Blender + # We want to retain the mount point, and the folder the mount is + # created on. The mounted disk we need for detaching, the folder we + # need to be able to copy the contents to where we can use them + attachment_items = attached.splitlines()[-1].split() + mounted_disk = attachment_items[0] + source_location = pathlib.Path(attachment_items[2], "Blender.app") + + print(f"{source_location} -> {dst}") + + shutil.copytree(source_location, dst) + + hdiutil_detach = ["hdiutil", + "detach", + f"{mounted_disk}" + ] + execute_command(hdiutil_detach, "hdiutil detach", 2) + + +def create_build_script(template_name: str, vars: List[Tuple[str, str]]) -> pathlib.Path: + """ + Create the Steam build script + + Use the given template and template variable tuple list. + + Returns pathlib.Path to the created script. + + Args: + template_name (str): [description] + vars (List[Tuple[str, str]]): [description] + + Returns: + pathlib.Path: Full path to the generated script + """ + build_script = pathlib.Path(".", template_name).read_text() + for var in vars: + build_script = build_script.replace(var[0], var[1]) + build_script_file = template_name.replace(".template", "") + build_script_path = pathlib.Path(".", build_script_file) + build_script_path.write_text(build_script) + return build_script_path + + +def clean_up() -> None: + """Remove intermediate files depending on given command-line arguments + """ + global content_location, args + + if not args.leavearch and not args.leaveextracted: + shutil.rmtree(content_location) + + if args.leavearch and not args.leaveextracted: + shutil.rmtree(content_location.joinpath(zip_extract_folder)) + shutil.rmtree(content_location.joinpath(tarxz_extract_folder)) + shutil.rmtree(content_location.joinpath(dmg_extract_folder)) + + if args.leaveextracted and not args.leavearch: + import os + os.remove(content_location.joinpath(zipped_blender)) + os.remove(content_location.joinpath(tarxz_blender)) + os.remove(content_location.joinpath(dmg_blender)) + + +def extract_archive(archive: str, extract_folder_name: str, + cmd: List[str], errcode: int) -> None: + """Extract all files from archive to given folder name. + + Will not extract if + target folder already exists, or if --skipextract was given on the + command-line. + + Args: + archive (str): Archive name to extract + extract_folder_name (str): Folder name to extract to + cmd (List[str]): Command with arguments to use + errcode (int): Error code to use for exit() + """ + global args, content_location + + extract_location = content_location.joinpath(extract_folder_name) + + pre_extract = set(content_location.glob("*")) + + if not args.skipextract or not extract_location.exists(): + print(f"Extracting files from {archive}...") + cmd.append(content_location.joinpath(archive)) + execute_command(cmd, cmd[0], errcode, cwd=content_location) + # in case we use a non-release archive the naming will be incorrect. + # simply rename to expected target name + post_extract = set(content_location.glob("*")) + diff_extract = post_extract - pre_extract + if not extract_location in diff_extract: + folder_to_rename = list(diff_extract)[0] + folder_to_rename.rename(extract_location) + print(" OK") + else: + print(f"Skipping extraction {archive}!") + +# ============================================================================== + + +parser = argparse.ArgumentParser() + +parser.add_argument("--baseurl", required=True, + help="The base URL for files to download, " + "i.e. https://download.blender.org/release/Blender2.83/") + +parser.add_argument("--version", required=True, + help="The Blender version to release, in the form 2.83.3") + +parser.add_argument("--appid", required=True, + help="The Blender App ID on Steam") +parser.add_argument("--winid", required=True, + help="The Windows depot ID") +parser.add_argument("--linuxid", required=True, + help="The Linux depot ID") +parser.add_argument("--macosid", required=True, + help="The MacOS depot ID") + +parser.add_argument("--steamcmd", required=True, + help="Path to the steamcmd") +parser.add_argument("--steamuser", required=True, + help="The login for the Steam builder user") +parser.add_argument("--steampw", required=True, + help="Login password for the Steam builder user") + +add_optional_argument("--dryrun", + "If set the Steam files will not be uploaded") +add_optional_argument("--leavearch", + help="If set don't clean up the downloaded archives") +add_optional_argument("--leaveextracted", + help="If set don't clean up the extraction folders") +add_optional_argument("--skipdl", + help="If set downloading the archives is skipped if it already exists locally.") +add_optional_argument("--skipextract", + help="If set skips extracting of archives. The tool assumes the archives" + "have already been extracted to their correct locations") + +args = parser.parse_args() + +VERSIONNODOTS = args.version.replace('.', '') +OUTPUT = f"output{VERSIONNODOTS}" +CONTENT = f"content{VERSIONNODOTS}" + +# ===== set up main locations + +content_location = pathlib.Path(".", CONTENT).absolute() +output_location = pathlib.Path(".", OUTPUT).absolute() + +content_location.mkdir(parents=True, exist_ok=True) +output_location.mkdir(parents=True, exist_ok=True) + +# ===== login + +# Logging into Steam once to ensure the SDK updates itself properly. If we don't +# do that the combined +login and +run_app_build_http at the end of the tool +# will fail. +steam_login = [args.steamcmd, + "+login", + args.steamuser, + args.steampw, + "+quit" + ] +print("Logging in to Steam...") +execute_command(steam_login, "Login to Steam", 10) +print(" OK") + +# ===== prepare Steam build scripts + +template_vars = [ + ("[APPID]", args.appid), + ("[OUTPUT]", OUTPUT), + ("[CONTENT]", CONTENT), + ("[VERSION]", args.version), + ("[WINID]", args.winid), + ("[LINUXID]", args.linuxid), + ("[MACOSID]", args.macosid), + ("[DRYRUN]", f"{args.dryrun}" if args.dryrun else "0") +] + +blender_app_build = create_build_script( + "blender_app_build.vdf.template", template_vars) +create_build_script("depot_build_win.vdf.template", template_vars) +create_build_script("depot_build_linux.vdf.template", template_vars) +create_build_script("depot_build_macos.vdf.template", template_vars) + +# ===== download archives + +download_archives(args.baseurl, blender_archives, + args.version, content_location) + +# ===== set up file and folder names + +zipped_blender = get_archive_type("zip", args.version) +zip_extract_folder = zipped_blender.replace(".zip", "") +tarxz_blender = get_archive_type("tar.xz", args.version) +tarxz_extract_folder = tarxz_blender.replace(".tar.xz", "") +dmg_blender = get_archive_type("dmg", args.version) +dmg_extract_folder = dmg_blender.replace(".dmg", "") + +# ===== extract + +unzip_cmd = ["unzip", "-q"] +extract_archive(zipped_blender, zip_extract_folder, unzip_cmd, 3) + +untarxz_cmd = ["tar", "-xf"] +extract_archive(tarxz_blender, tarxz_extract_folder, untarxz_cmd, 4) + +if not args.skipextract or not content_location.joinpath(dmg_extract_folder).exists(): + print("Extracting files from Blender MacOS archive...") + blender_dmg = content_location.joinpath(dmg_blender) + target_location = content_location.joinpath( + dmg_extract_folder, "Blender.app") + copy_contents_from_dmg_to_path(blender_dmg, target_location) + print(" OK") +else: + print("Skipping extraction of .dmg!") + +# ===== building + +print("Build Steam game files...") +steam_build = [args.steamcmd, + "+login", + args.steamuser, + args.steampw, + "+run_app_build_http", + blender_app_build.absolute(), + "+quit" + ] +execute_command(steam_build, "Build with steamcmd", 13) +print(" OK") + +clean_up() diff --git a/release/steam/depot_build_linux.vdf.template b/release/steam/depot_build_linux.vdf.template new file mode 100644 index 00000000000..0f69008548e --- /dev/null +++ b/release/steam/depot_build_linux.vdf.template @@ -0,0 +1,31 @@ +"DepotBuildConfig" +{ + // Set your assigned depot ID here + "DepotID" "[LINUXID]" + + // Set a root for all content. + // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) + // will be resolved relative to this root. + // If you don't define ContentRoot, then it will be assumed to be + // the location of this script file, which probably isn't what you want + "ContentRoot" "./blender-[VERSION]-linux64/" + + // include all files recursivley + "FileMapping" + { + // This can be a full path, or a path relative to ContentRoot + "LocalPath" "*" + + // This is a path relative to the install folder of your game + "DepotPath" "." + + // If LocalPath contains wildcards, setting this means that all + // matching files within subdirectories of LocalPath will also + // be included. + "recursive" "1" + } + + // but exclude all symbol files + // This can be a full path, or a path relative to ContentRoot + "FileExclusion" "*.pdb" +} diff --git a/release/steam/depot_build_macos.vdf.template b/release/steam/depot_build_macos.vdf.template new file mode 100644 index 00000000000..33dde860462 --- /dev/null +++ b/release/steam/depot_build_macos.vdf.template @@ -0,0 +1,30 @@ +"DepotBuildConfig" +{ + // Set your assigned depot ID here + "DepotID" "[MACOSID]" + + // Set a root for all content. + // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) + // will be resolved relative to this root. + // If you don't define ContentRoot, then it will be assumed to be + // the location of this script file, which probably isn't what you want + "ContentRoot" "./blender-[VERSION]-macOS/" + // include all files recursivley + "FileMapping" + { + // This can be a full path, or a path relative to ContentRoot + "LocalPath" "*" + + // This is a path relative to the install folder of your game + "DepotPath" "." + + // If LocalPath contains wildcards, setting this means that all + // matching files within subdirectories of LocalPath will also + // be included. + "recursive" "1" + } + + // but exclude all symbol files + // This can be a full path, or a path relative to ContentRoot + "FileExclusion" "*.pdb" +} diff --git a/release/steam/depot_build_win.vdf.template b/release/steam/depot_build_win.vdf.template new file mode 100644 index 00000000000..2c18a0f15dd --- /dev/null +++ b/release/steam/depot_build_win.vdf.template @@ -0,0 +1,31 @@ +"DepotBuildConfig" +{ + // Set your assigned depot ID here + "DepotID" "[WINID]" + + // Set a root for all content. + // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths) + // will be resolved relative to this root. + // If you don't define ContentRoot, then it will be assumed to be + // the location of this script file, which probably isn't what you want + "ContentRoot" "./blender-[VERSION]-windows64/" + + // include all files recursivley + "FileMapping" + { + // This can be a full path, or a path relative to ContentRoot + "LocalPath" "*" + + // This is a path relative to the install folder of your game + "DepotPath" "." + + // If LocalPath contains wildcards, setting this means that all + // matching files within subdirectories of LocalPath will also + // be included. + "recursive" "1" + } + + // but exclude all symbol files + // This can be a full path, or a path relative to ContentRoot + "FileExclusion" "*.pdb" +} diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index da6df831c0a..efd30ba8509 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -23,7 +23,9 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_action_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_anim_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_asset_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_boid_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_enums.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cachefile_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_camera_types.h diff --git a/source/blender/blenfont/intern/blf_dir.c b/source/blender/blenfont/intern/blf_dir.c index 51d3849aa48..25235097505 100644 --- a/source/blender/blenfont/intern/blf_dir.c +++ b/source/blender/blenfont/intern/blf_dir.c @@ -47,6 +47,9 @@ #include "blf_internal.h" #include "blf_internal_types.h" +#include "BKE_global.h" +#include "BKE_main.h" + static ListBase global_font_dir = {NULL, NULL}; static DirBLF *blf_dir_find(const char *path) @@ -137,9 +140,11 @@ char *blf_dir_search(const char *file) } if (!s) { - /* check the current directory, why not ? */ - if (BLI_exists(file)) { - s = BLI_strdup(file); + /* Assume file is either an abslute path, or a relative path to current directory. */ + BLI_strncpy(full_path, file, sizeof(full_path)); + BLI_path_abs(full_path, BKE_main_blendfile_path(G_MAIN)); + if (BLI_exists(full_path)) { + s = BLI_strdup(full_path); } } diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index d8605941974..717cfa607ad 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -30,10 +30,10 @@ extern "C" { #endif -struct BlendWriter; struct BlendDataReader; -struct BlendLibReader; struct BlendExpander; +struct BlendLibReader; +struct BlendWriter; struct bArmature; /* The following structures are defined in DNA_action_types.h, and DNA_anim_types.h */ diff --git a/source/blender/blenkernel/BKE_anim_visualization.h b/source/blender/blenkernel/BKE_anim_visualization.h index decb2e0b210..4e86abeed8d 100644 --- a/source/blender/blenkernel/BKE_anim_visualization.h +++ b/source/blender/blenkernel/BKE_anim_visualization.h @@ -26,14 +26,14 @@ extern "C" { #endif +struct BlendDataReader; +struct BlendWriter; struct Object; struct ReportList; struct Scene; struct bAnimVizSettings; struct bMotionPath; struct bPoseChannel; -struct BlendWriter; -struct BlendDataReader; /* ---------------------------------------------------- */ /* Animation Visualization */ diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index e812d04c7d1..8d904bd6019 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -45,8 +45,6 @@ struct NlaKeyframingContext; struct PathResolvedRNA; struct PointerRNA; struct PropertyRNA; -struct ReportList; -struct Scene; struct bAction; struct bActionGroup; struct bContext; diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h index 6da6079ea2a..c9d671597e8 100644 --- a/source/blender/blenkernel/BKE_appdir.h +++ b/source/blender/blenkernel/BKE_appdir.h @@ -31,6 +31,8 @@ void BKE_appdir_exit(void); /* note on naming: typical _get() suffix is omitted here, * since its the main purpose of the API. */ const char *BKE_appdir_folder_default(void); +const char *BKE_appdir_folder_home(void); +bool BKE_appdir_folder_documents(char *dir); bool BKE_appdir_folder_id_ex(const int folder_id, const char *subfolder, char *path, diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h new file mode 100644 index 00000000000..38cd5747343 --- /dev/null +++ b/source/blender/blenkernel/BKE_asset.h @@ -0,0 +1,59 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BKE_ASSET_H__ +#define __BKE_ASSET_H__ + +/** \file + * \ingroup bke + */ + +#include "BLI_utildefines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct BlendDataReader; +struct BlendWriter; +struct ID; +struct PreviewImage; + +struct AssetMetaData *BKE_asset_metadata_create(void); +void BKE_asset_metadata_free(struct AssetMetaData **asset_data); + +struct AssetTagEnsureResult { + struct AssetTag *tag; + /* Set to false if a tag of this name was already present. */ + bool is_new; +}; + +struct AssetTag *BKE_asset_metadata_tag_add(struct AssetMetaData *asset_data, const char *name); +struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(struct AssetMetaData *asset_data, + const char *name); +void BKE_asset_metadata_tag_remove(struct AssetMetaData *asset_data, struct AssetTag *tag); + +struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data, + const struct ID *owner_id); + +void BKE_asset_metadata_write(struct BlendWriter *writer, struct AssetMetaData *asset_data); +void BKE_asset_metadata_read(struct BlendDataReader *reader, struct AssetMetaData *asset_data); + +#ifdef __cplusplus +} +#endif + +#endif /* __BKE_ASSET_H__ */ diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 55a841d8fd1..574d9904dc4 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -35,7 +35,6 @@ extern "C" { struct CustomData; struct CustomDataLayer; struct ID; -struct PointerRNA; struct ReportList; /* Attribute.domain */ diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index c4a704ef385..22e14e44bec 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -26,8 +26,6 @@ #include "BLI_color.hh" #include "BLI_float3.hh" -struct Mesh; - namespace blender::bke { using fn::CPPType; @@ -266,11 +264,15 @@ template<typename T> class TypedWriteAttribute { } }; +using BooleanReadAttribute = TypedReadAttribute<bool>; using FloatReadAttribute = TypedReadAttribute<float>; using Float3ReadAttribute = TypedReadAttribute<float3>; +using Int32ReadAttribute = TypedReadAttribute<int>; using Color4fReadAttribute = TypedReadAttribute<Color4f>; +using BooleanWriteAttribute = TypedWriteAttribute<bool>; using FloatWriteAttribute = TypedWriteAttribute<float>; using Float3WriteAttribute = TypedWriteAttribute<float3>; +using Int32WriteAttribute = TypedWriteAttribute<int>; using Color4fWriteAttribute = TypedWriteAttribute<Color4f>; } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index afb6112b954..1ed4d1183a1 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 6 +#define BLENDER_FILE_SUBVERSION 8 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_boids.h b/source/blender/blenkernel/BKE_boids.h index c9e6f0e7346..71a4d35767f 100644 --- a/source/blender/blenkernel/BKE_boids.h +++ b/source/blender/blenkernel/BKE_boids.h @@ -23,13 +23,16 @@ * \ingroup bke */ -#include "DNA_boid_types.h" -#include "DNA_particle_types.h" - #ifdef __cplusplus extern "C" { #endif +struct BoidSettings; +struct BoidState; +struct Object; +struct ParticleData; +struct ParticleSettings; +struct ParticleSimulationData; struct RNG; typedef struct BoidBrainData { @@ -50,13 +53,13 @@ typedef struct BoidBrainData { void boids_precalc_rules(struct ParticleSettings *part, float cfra); void boid_brain(BoidBrainData *bbd, int p, struct ParticleData *pa); void boid_body(BoidBrainData *bbd, struct ParticleData *pa); -void boid_default_settings(BoidSettings *boids); -BoidRule *boid_new_rule(int type); -BoidState *boid_new_state(BoidSettings *boids); -BoidState *boid_duplicate_state(BoidSettings *boids, BoidState *state); -void boid_free_settings(BoidSettings *boids); -BoidSettings *boid_copy_settings(const BoidSettings *boids); -BoidState *boid_get_current_state(BoidSettings *boids); +void boid_default_settings(struct BoidSettings *boids); +struct BoidRule *boid_new_rule(int type); +struct BoidState *boid_new_state(struct BoidSettings *boids); +struct BoidState *boid_duplicate_state(struct BoidSettings *boids, struct BoidState *state); +void boid_free_settings(struct BoidSettings *boids); +struct BoidSettings *boid_copy_settings(const struct BoidSettings *boids); +struct BoidState *boid_get_current_state(struct BoidSettings *boids); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_collision.h b/source/blender/blenkernel/BKE_collision.h index 3a5328a33e2..ff1bca896b1 100644 --- a/source/blender/blenkernel/BKE_collision.h +++ b/source/blender/blenkernel/BKE_collision.h @@ -22,21 +22,11 @@ * \ingroup bke */ -#include <float.h> -#include <math.h> -#include <stdlib.h> -#include <string.h> - -/* types */ -#include "BKE_collision.h" -#include "DNA_cloth_types.h" - -#include "BLI_kdopbvh.h" - #ifdef __cplusplus extern "C" { #endif +struct BVHTree; struct Collection; struct CollisionModifierData; struct Depsgraph; @@ -113,11 +103,11 @@ typedef struct FaceCollPair { // used in modifier.c from collision.c ///////////////////////////////////////////////// -BVHTree *bvhtree_build_from_mvert(const struct MVert *mvert, - const struct MVertTri *tri, - int tri_num, - float epsilon); -void bvhtree_update_from_mvert(BVHTree *bvhtree, +struct BVHTree *bvhtree_build_from_mvert(const struct MVert *mvert, + const struct MVertTri *tri, + int tri_num, + float epsilon); +void bvhtree_update_from_mvert(struct BVHTree *bvhtree, const struct MVert *mvert, const struct MVert *mvert_moving, const struct MVertTri *tri, diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index 7a14787c191..afad1e26159 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -23,6 +23,10 @@ * \ingroup bke */ +struct BlendDataReader; +struct BlendExpander; +struct BlendLibReader; +struct BlendWriter; struct Depsgraph; struct ID; struct ListBase; @@ -31,10 +35,6 @@ struct Scene; struct bConstraint; struct bConstraintTarget; struct bPoseChannel; -struct BlendWriter; -struct BlendDataReader; -struct BlendLibReader; -struct BlendExpander; /* ---------------------------------------------------------------------------- */ #ifdef __cplusplus diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 5c534803781..94392dd78da 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -143,8 +143,9 @@ bContext *CTX_copy(const bContext *C); /* Stored Context */ -bContextStore *CTX_store_add(ListBase *contexts, const char *name, PointerRNA *ptr); +bContextStore *CTX_store_add(ListBase *contexts, const char *name, const PointerRNA *ptr); bContextStore *CTX_store_add_all(ListBase *contexts, bContextStore *context); +bContextStore *CTX_store_get(bContext *C); void CTX_store_set(bContext *C, bContextStore *store); bContextStore *CTX_store_copy(bContextStore *store); void CTX_store_free(bContextStore *store); @@ -288,6 +289,9 @@ enum eContextObjectMode CTX_data_mode_enum(const bContext *C); void CTX_data_main_set(bContext *C, struct Main *bmain); void CTX_data_scene_set(bContext *C, struct Scene *scene); +/* Only Outliner currently! */ +int CTX_data_selected_ids(const bContext *C, ListBase *list); + int CTX_data_selected_editable_objects(const bContext *C, ListBase *list); int CTX_data_selected_editable_bases(const bContext *C, ListBase *list); diff --git a/source/blender/blenkernel/BKE_cryptomatte.h b/source/blender/blenkernel/BKE_cryptomatte.h index 9ad4770c754..433c25084ad 100644 --- a/source/blender/blenkernel/BKE_cryptomatte.h +++ b/source/blender/blenkernel/BKE_cryptomatte.h @@ -29,14 +29,21 @@ extern "C" { #endif -struct Object; +struct Main; struct Material; +struct Object; +uint32_t BKE_cryptomatte_hash(const char *name, int name_len); uint32_t BKE_cryptomatte_object_hash(const struct Object *object); uint32_t BKE_cryptomatte_material_hash(const struct Material *material); uint32_t BKE_cryptomatte_asset_hash(const struct Object *object); float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash); +char *BKE_cryptomatte_entries_to_matte_id(struct NodeCryptomatte *node_storage); +void BKE_cryptomatte_matte_id_to_entries(const struct Main *bmain, + struct NodeCryptomatte *node_storage, + const char *matte_id); + #ifdef __cplusplus } -#endif
\ No newline at end of file +#endif diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index dcb4a993da1..881b93fe709 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -22,7 +22,9 @@ * \ingroup bke */ -#include "DNA_scene_types.h" +#include "BLI_sys_types.h" + +#include "DNA_listBase.h" #ifdef __cplusplus extern "C" { @@ -318,8 +320,8 @@ void BKE_curve_deform_coords(const struct Object *ob_curve, const short flag, const short defaxis); -void BKE_curve_deform_coords_with_editmesh(const Object *ob_curve, - const Object *ob_target, +void BKE_curve_deform_coords_with_editmesh(const struct Object *ob_curve, + const struct Object *ob_target, float (*vert_coords)[3], const int vert_coords_len, const int defgrp_index, diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 0c84ad70845..2fb713a4299 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -35,9 +35,7 @@ struct BMLoop; struct BMesh; struct BoundBox; struct Depsgraph; -struct EditMeshData; struct Mesh; -struct MeshStatVis; struct Object; struct Scene; diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h index 0585f67703c..3cba47afc46 100644 --- a/source/blender/blenkernel/BKE_effect.h +++ b/source/blender/blenkernel/BKE_effect.h @@ -22,8 +22,6 @@ * \ingroup bke */ -#include "DNA_modifier_types.h" - #include "BLI_utildefines.h" #ifdef __cplusplus @@ -147,7 +145,7 @@ float effector_falloff(struct EffectorCache *eff, struct EffectorData *efd, struct EffectedPoint *point, struct EffectorWeights *weights); -int closest_point_on_surface(SurfaceModifierData *surmd, +int closest_point_on_surface(struct SurfaceModifierData *surmd, const float co[3], float surface_co[3], float surface_nor[3], diff --git a/source/blender/blenkernel/BKE_freestyle.h b/source/blender/blenkernel/BKE_freestyle.h index 47f0b547d83..5e29665d728 100644 --- a/source/blender/blenkernel/BKE_freestyle.h +++ b/source/blender/blenkernel/BKE_freestyle.h @@ -23,8 +23,6 @@ * \ingroup bke */ -#include "DNA_scene_types.h" - #ifdef __cplusplus extern "C" { #endif @@ -39,28 +37,31 @@ typedef struct FreestyleModuleSettings FreestyleModuleSettings; typedef struct FreestyleSettings FreestyleSettings; /* FreestyleConfig */ -void BKE_freestyle_config_init(FreestyleConfig *config); -void BKE_freestyle_config_free(FreestyleConfig *config, const bool do_id_user); -void BKE_freestyle_config_copy(FreestyleConfig *new_config, - const FreestyleConfig *config, +void BKE_freestyle_config_init(struct FreestyleConfig *config); +void BKE_freestyle_config_free(struct FreestyleConfig *config, const bool do_id_user); +void BKE_freestyle_config_copy(struct FreestyleConfig *new_config, + const struct FreestyleConfig *config, const int flag); /* FreestyleConfig.modules */ -FreestyleModuleConfig *BKE_freestyle_module_add(FreestyleConfig *config); -bool BKE_freestyle_module_delete(FreestyleConfig *config, FreestyleModuleConfig *module_conf); -bool BKE_freestyle_module_move(FreestyleConfig *config, - FreestyleModuleConfig *module_conf, +struct FreestyleModuleConfig *BKE_freestyle_module_add(struct FreestyleConfig *config); +bool BKE_freestyle_module_delete(struct FreestyleConfig *config, + struct FreestyleModuleConfig *module_conf); +bool BKE_freestyle_module_move(struct FreestyleConfig *config, + struct FreestyleModuleConfig *module_conf, int direction); /* FreestyleConfig.linesets */ -FreestyleLineSet *BKE_freestyle_lineset_add(struct Main *bmain, - FreestyleConfig *config, - const char *name); -bool BKE_freestyle_lineset_delete(FreestyleConfig *config, FreestyleLineSet *lineset); -FreestyleLineSet *BKE_freestyle_lineset_get_active(FreestyleConfig *config); -short BKE_freestyle_lineset_get_active_index(FreestyleConfig *config); -void BKE_freestyle_lineset_set_active_index(FreestyleConfig *config, short index); -void BKE_freestyle_lineset_unique_name(FreestyleConfig *config, FreestyleLineSet *lineset); +struct FreestyleLineSet *BKE_freestyle_lineset_add(struct Main *bmain, + struct FreestyleConfig *config, + const char *name); +bool BKE_freestyle_lineset_delete(struct FreestyleConfig *config, + struct FreestyleLineSet *lineset); +struct FreestyleLineSet *BKE_freestyle_lineset_get_active(struct FreestyleConfig *config); +short BKE_freestyle_lineset_get_active_index(struct FreestyleConfig *config); +void BKE_freestyle_lineset_set_active_index(struct FreestyleConfig *config, short index); +void BKE_freestyle_lineset_unique_name(struct FreestyleConfig *config, + struct FreestyleLineSet *lineset); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h index 026f4d39d51..37a3ed82bb8 100644 --- a/source/blender/blenkernel/BKE_geometry_set.h +++ b/source/blender/blenkernel/BKE_geometry_set.h @@ -24,18 +24,32 @@ extern "C" { #endif -struct Object; +struct Collection; struct GeometrySet; +struct Object; void BKE_geometry_set_free(struct GeometrySet *geometry_set); bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set); +typedef enum InstancedDataType { + INSTANCE_DATA_TYPE_OBJECT = 0, + INSTANCE_DATA_TYPE_COLLECTION = 1, +} InstancedDataType; + +typedef struct InstancedData { + InstancedDataType type; + union { + struct Object *object; + struct Collection *collection; + } data; +} InstancedData; + int BKE_geometry_set_instances(const struct GeometrySet *geometry_set, float (**r_positions)[3], float (**r_rotations)[3], float (**r_scales)[3], - struct Object ***r_objects); + struct InstancedData **r_instanced_data); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 3398da9896b..e4232a84a00 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -32,9 +32,10 @@ #include "BKE_attribute_access.hh" #include "BKE_geometry_set.h" +struct Collection; struct Mesh; -struct PointCloud; struct Object; +struct PointCloud; /* Each geometry component has a specific type. The type determines what kind of data the component * stores. Functions modifying a geometry will usually just modify a subset of the component types. @@ -88,6 +89,9 @@ class GeometryComponent { GeometryComponentType type() const; + /* Return true when any attribute with this name exists, including built in attributes. */ + bool attribute_exists(const blender::StringRef attribute_name) const; + /* Returns true when the geometry component supports this attribute domain. */ virtual bool attribute_domain_supported(const AttributeDomain domain) const; /* Returns true when the given data type is supported in the given domain. */ @@ -132,6 +136,11 @@ class GeometryComponent { const AttributeDomain domain, const CustomDataType data_type) const; + /* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged. + * Returns null when the attribute does not exist. */ + blender::bke::ReadAttributePtr attribute_try_get_for_read( + const blender::StringRef attribute_name, const AttributeDomain domain) const; + /* Get a read-only attribute for the given domain and data type. * Returns a constant attribute based on the default value if the attribute does not exist. * Never returns null. */ @@ -355,7 +364,7 @@ class InstancesComponent : public GeometryComponent { blender::Vector<blender::float3> positions_; blender::Vector<blender::float3> rotations_; blender::Vector<blender::float3> scales_; - blender::Vector<const Object *> objects_; + blender::Vector<InstancedData> instanced_data_; public: InstancesComponent(); @@ -363,12 +372,20 @@ class InstancesComponent : public GeometryComponent { GeometryComponent *copy() const override; void clear(); - void add_instance(const Object *object, + void add_instance(Object *object, blender::float3 position, blender::float3 rotation = {0, 0, 0}, blender::float3 scale = {1, 1, 1}); + void add_instance(Collection *collection, + blender::float3 position, + blender::float3 rotation = {0, 0, 0}, + blender::float3 scale = {1, 1, 1}); + void add_instance(InstancedData data, + blender::float3 position, + blender::float3 rotation, + blender::float3 scale); - blender::Span<const Object *> objects() const; + blender::Span<InstancedData> instanced_data() const; blender::Span<blender::float3> positions() const; blender::Span<blender::float3> rotations() const; blender::Span<blender::float3> scales() const; diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 6dc8d1ef06e..df5711f5120 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -36,19 +36,17 @@ struct ListBase; struct MDeformVert; struct Main; struct Material; -struct MaterialGPencilStyle; struct Object; struct Scene; struct SpaceImage; struct ToolSettings; struct ViewLayer; struct bDeformGroup; +struct bGPDcurve; struct bGPDframe; struct bGPDlayer; struct bGPDlayer_Mask; -struct bGPDspoint; struct bGPDstroke; -struct bGPDcurve; struct bGPdata; #define GPENCIL_SIMPLIFY(scene) ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE)) diff --git a/source/blender/blenkernel/BKE_gpencil_curve.h b/source/blender/blenkernel/BKE_gpencil_curve.h index 1821972469c..2d42bb36949 100644 --- a/source/blender/blenkernel/BKE_gpencil_curve.h +++ b/source/blender/blenkernel/BKE_gpencil_curve.h @@ -30,10 +30,10 @@ extern "C" { struct Main; struct Object; struct Scene; -struct bGPdata; +struct bGPDcurve; struct bGPDlayer; struct bGPDstroke; -struct bGPDcurve; +struct bGPdata; void BKE_gpencil_convert_curve(struct Main *bmain, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index b107a6e72af..1c86df73d3c 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -32,12 +32,11 @@ struct Depsgraph; struct Main; struct Object; struct Scene; +struct bGPDcurve; struct bGPDframe; -struct bGPDlayer; struct bGPDspoint; struct bGPDstroke; struct bGPdata; -struct bGPDcurve; /* Object boundbox. */ bool BKE_gpencil_data_minmax(const struct bGPdata *gpd, float r_min[3], float r_max[3]); @@ -164,6 +163,11 @@ bool BKE_gpencil_convert_mesh(struct Main *bmain, const bool use_seams, const bool use_faces); +void BKE_gpencil_stroke_uniform_subdivide(struct bGPdata *gpd, + struct bGPDstroke *gps, + const uint32_t target_number, + const bool select); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h index ccf65a585ef..61ccf3d60f6 100644 --- a/source/blender/blenkernel/BKE_gpencil_modifier.h +++ b/source/blender/blenkernel/BKE_gpencil_modifier.h @@ -27,6 +27,9 @@ extern "C" { #endif struct ARegionType; +struct BlendDataReader; +struct BlendLibReader; +struct BlendWriter; struct Depsgraph; struct GpencilModifierData; struct ID; @@ -35,9 +38,6 @@ struct Main; struct ModifierUpdateDepsgraphContext; struct Object; struct Scene; -struct BlendWriter; -struct BlendDataReader; -struct BlendLibReader; /* NOTE: bakeModifier() called from UI: * needs to create new data-blocks, hence the need for this. */ struct bGPDframe; diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h index d6cf5eae323..28a6f837f61 100644 --- a/source/blender/blenkernel/BKE_icons.h +++ b/source/blender/blenkernel/BKE_icons.h @@ -23,17 +23,26 @@ * \ingroup bke * * Resizable Icons for Blender + * + * There is some thread safety for this API but it is rather weak. Registering or unregistering + * icons is thread safe, changing data of icons from multiple threads is not. Practically this + * should be fine since only the main thread modifies icons. Should that change, more locks or a + * different design need to be introduced. */ #ifdef __cplusplus extern "C" { #endif +#include "BLI_compiler_attrs.h" + typedef void (*DrawInfoFreeFP)(void *drawinfo); enum { /** ID preview: obj is #ID. */ ICON_DATA_ID = 0, + /** Arbitrary Image buffer: obj is #ImBuf */ + ICON_DATA_IMBUF, /** Preview: obj is #PreviewImage */ ICON_DATA_PREVIEW, /** 2D triangles: obj is #Icon_Geom */ @@ -44,6 +53,9 @@ enum { ICON_DATA_GPLAYER, }; +/** + * \note See comment at the top regarding thread safety. + */ struct Icon { void *drawinfo; /** @@ -93,6 +105,9 @@ int BKE_icon_gplayer_color_ensure(struct bGPDlayer *gpl); int BKE_icon_preview_ensure(struct ID *id, struct PreviewImage *preview); +int BKE_icon_imbuf_create(struct ImBuf *ibuf) ATTR_WARN_UNUSED_RESULT; +struct ImBuf *BKE_icon_imbuf_get_buffer(int icon_id) ATTR_WARN_UNUSED_RESULT; + /* retrieve icon for id */ struct Icon *BKE_icon_get(const int icon_id); @@ -129,6 +144,12 @@ void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size) /* get the preview from any pointer */ struct PreviewImage **BKE_previewimg_id_get_p(const struct ID *id); +struct PreviewImage *BKE_previewimg_id_get(const struct ID *id); + +bool BKE_previewimg_id_supports_jobs(const struct ID *id); + +/* Trigger deferred loading of a custom image file into the preview buffer. */ +void BKE_previewimg_id_custom_set(struct ID *id, const char *path); /* free the preview image belonging to the id */ void BKE_previewimg_id_free(struct ID *id); @@ -146,6 +167,11 @@ struct PreviewImage *BKE_previewimg_id_ensure(struct ID *id); void BKE_previewimg_ensure(struct PreviewImage *prv, const int size); +struct ImBuf *BKE_previewimg_to_imbuf(struct PreviewImage *prv, const int size); + +void BKE_previewimg_finish(struct PreviewImage *prv, const int size); +bool BKE_previewimg_is_finished(const struct PreviewImage *prv, const int size); + struct PreviewImage *BKE_previewimg_cached_get(const char *name); struct PreviewImage *BKE_previewimg_cached_ensure(const char *name); @@ -156,13 +182,14 @@ struct PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, bool force_update); void BKE_previewimg_cached_release(const char *name); -void BKE_previewimg_cached_release_pointer(struct PreviewImage *prv); + +void BKE_previewimg_deferred_release(struct PreviewImage *prv); void BKE_previewimg_blend_write(struct BlendWriter *writer, const struct PreviewImage *prv); void BKE_previewimg_blend_read(struct BlendDataReader *reader, struct PreviewImage *prv); int BKE_icon_geom_ensure(struct Icon_Geom *geom); -struct Icon_Geom *BKE_icon_geom_from_memory(const uchar *data, size_t data_len); +struct Icon_Geom *BKE_icon_geom_from_memory(uchar *data, size_t data_len); struct Icon_Geom *BKE_icon_geom_from_file(const char *filename); struct ImBuf *BKE_icon_geom_rasterize(const struct Icon_Geom *geom, diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 9c250240e5e..bcf35bf1197 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -20,8 +20,6 @@ * \ingroup bke */ -#include "DNA_ID.h" - #include "BLI_compiler_attrs.h" #ifdef __cplusplus @@ -57,9 +55,9 @@ typedef union IDPropertyTemplate { /* ----------- Property Array Type ---------- */ -IDProperty *IDP_NewIDPArray(const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -IDProperty *IDP_CopyIDPArray(const IDProperty *array, const int flag) ATTR_WARN_UNUSED_RESULT - ATTR_NONNULL(); +struct IDProperty *IDP_NewIDPArray(const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +struct IDProperty *IDP_CopyIDPArray(const struct IDProperty *array, + const int flag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); /* shallow copies item */ void IDP_SetIndexArray(struct IDProperty *prop, int index, struct IDProperty *item) ATTR_NONNULL(); @@ -74,7 +72,9 @@ void IDP_ResizeArray(struct IDProperty *prop, int newlen); void IDP_FreeArray(struct IDProperty *prop); /* ---------- String Type ------------ */ -IDProperty *IDP_NewString(const char *st, const char *name, int maxlen) ATTR_WARN_UNUSED_RESULT +struct IDProperty *IDP_NewString(const char *st, + const char *name, + int maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2 /* 'name 'arg */); /* maxlen excludes '\0' */ void IDP_AssignString(struct IDProperty *prop, const char *st, int maxlen) ATTR_NONNULL(); /* maxlen excludes '\0' */ @@ -84,9 +84,9 @@ void IDP_FreeString(struct IDProperty *prop) ATTR_NONNULL(); /*-------- ID Type -------*/ -typedef void (*IDPWalkFunc)(void *userData, IDProperty *idp); +typedef void (*IDPWalkFunc)(void *userData, struct IDProperty *idp); -void IDP_AssignID(IDProperty *prop, ID *id, const int flag); +void IDP_AssignID(struct IDProperty *prop, struct ID *id, const int flag); /*-------- Group Functions -------*/ @@ -100,10 +100,10 @@ void IDP_ReplaceInGroup(struct IDProperty *group, struct IDProperty *prop) ATTR_ void IDP_ReplaceInGroup_ex(struct IDProperty *group, struct IDProperty *prop, struct IDProperty *prop_exist); -void IDP_MergeGroup(IDProperty *dest, const IDProperty *src, const bool do_overwrite) +void IDP_MergeGroup(struct IDProperty *dest, const struct IDProperty *src, const bool do_overwrite) ATTR_NONNULL(); -void IDP_MergeGroup_ex(IDProperty *dest, - const IDProperty *src, +void IDP_MergeGroup_ex(struct IDProperty *dest, + const struct IDProperty *src, const bool do_overwrite, const int flag) ATTR_NONNULL(); bool IDP_AddToGroup(struct IDProperty *group, struct IDProperty *prop) ATTR_NONNULL(); @@ -113,11 +113,13 @@ bool IDP_InsertToGroup(struct IDProperty *group, void IDP_RemoveFromGroup(struct IDProperty *group, struct IDProperty *prop) ATTR_NONNULL(); void IDP_FreeFromGroup(struct IDProperty *group, struct IDProperty *prop) ATTR_NONNULL(); -IDProperty *IDP_GetPropertyFromGroup(const struct IDProperty *prop, - const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -IDProperty *IDP_GetPropertyTypeFromGroup(const struct IDProperty *prop, - const char *name, - const char type) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +struct IDProperty *IDP_GetPropertyFromGroup(const struct IDProperty *prop, + const char *name) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); +struct IDProperty *IDP_GetPropertyTypeFromGroup(const struct IDProperty *prop, + const char *name, + const char type) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); /*-------- Main Functions --------*/ struct IDProperty *IDP_GetProperties(struct ID *id, @@ -127,10 +129,10 @@ struct IDProperty *IDP_CopyProperty(const struct IDProperty *prop) ATTR_WARN_UNU ATTR_NONNULL(); struct IDProperty *IDP_CopyProperty_ex(const struct IDProperty *prop, const int flag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -void IDP_CopyPropertyContent(IDProperty *dst, IDProperty *src) ATTR_NONNULL(); +void IDP_CopyPropertyContent(struct IDProperty *dst, struct IDProperty *src) ATTR_NONNULL(); -bool IDP_EqualsProperties_ex(IDProperty *prop1, - IDProperty *prop2, +bool IDP_EqualsProperties_ex(struct IDProperty *prop1, + struct IDProperty *prop2, const bool is_strict) ATTR_WARN_UNUSED_RESULT; bool IDP_EqualsProperties(struct IDProperty *prop1, @@ -142,12 +144,12 @@ struct IDProperty *IDP_New(const char type, void IDP_FreePropertyContent_ex(struct IDProperty *prop, const bool do_id_user); void IDP_FreePropertyContent(struct IDProperty *prop); -void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user); +void IDP_FreeProperty_ex(struct IDProperty *prop, const bool do_id_user); void IDP_FreeProperty(struct IDProperty *prop); -void IDP_ClearProperty(IDProperty *prop); +void IDP_ClearProperty(struct IDProperty *prop); -void IDP_Reset(IDProperty *prop, const IDProperty *reference); +void IDP_Reset(struct IDProperty *prop, const struct IDProperty *reference); #define IDP_Int(prop) ((prop)->data.val) #define IDP_Array(prop) ((prop)->data.pointer) @@ -155,29 +157,29 @@ void IDP_Reset(IDProperty *prop, const IDProperty *reference); #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) # define IDP_Float(prop) \ _Generic((prop), \ - IDProperty *: (*(float *)&(prop)->data.val), \ - const IDProperty *: (*(const float *)&(prop)->data.val)) + struct IDProperty *: (*(float *)&(prop)->data.val), \ + const struct IDProperty *: (*(const float *)&(prop)->data.val)) # define IDP_Double(prop) \ _Generic((prop), \ - IDProperty *: (*(double *)&(prop)->data.val), \ - const IDProperty *: (*(const double *)&(prop)->data.val)) + struct IDProperty *: (*(double *)&(prop)->data.val), \ + const struct IDProperty *: (*(const double *)&(prop)->data.val)) # define IDP_String(prop) \ _Generic((prop), \ - IDProperty *: ((char *) (prop)->data.pointer), \ - const IDProperty *: ((const char *) (prop)->data.pointer)) + struct IDProperty *: ((char *) (prop)->data.pointer), \ + const struct IDProperty *: ((const char *) (prop)->data.pointer)) # define IDP_IDPArray(prop) \ _Generic((prop), \ - IDProperty *: ((IDProperty *) (prop)->data.pointer), \ - const IDProperty *: ((const IDProperty *) (prop)->data.pointer)) + struct IDProperty *: ((struct IDProperty *) (prop)->data.pointer), \ + const struct IDProperty *: ((const struct IDProperty *) (prop)->data.pointer)) # define IDP_Id(prop) \ _Generic((prop), \ - IDProperty *: ((ID *) (prop)->data.pointer), \ - const IDProperty *: ((const ID *) (prop)->data.pointer)) + struct IDProperty *: ((ID *) (prop)->data.pointer), \ + const struct IDProperty *: ((const ID *) (prop)->data.pointer)) #else # define IDP_Float(prop) (*(float *)&(prop)->data.val) # define IDP_Double(prop) (*(double *)&(prop)->data.val) # define IDP_String(prop) ((char *)(prop)->data.pointer) -# define IDP_IDPArray(prop) ((IDProperty *)(prop)->data.pointer) +# define IDP_IDPArray(prop) ((struct IDProperty *)(prop)->data.pointer) # define IDP_Id(prop) ((ID *)(prop)->data.pointer) #endif @@ -185,7 +187,7 @@ void IDP_Reset(IDProperty *prop, const IDProperty *reference); * Call a callback for each idproperty in the hierarchy under given root one (included). * */ -typedef void (*IDPForeachPropertyCallback)(IDProperty *id_property, void *user_data); +typedef void (*IDPForeachPropertyCallback)(struct IDProperty *id_property, void *user_data); void IDP_foreach_property(struct IDProperty *id_property_root, const int type_filter, @@ -194,18 +196,18 @@ void IDP_foreach_property(struct IDProperty *id_property_root, /* Format IDProperty as strings */ char *IDP_reprN(const struct IDProperty *prop, uint *r_len); -void IDP_repr_fn(const IDProperty *prop, +void IDP_repr_fn(const struct IDProperty *prop, void (*str_append_fn)(void *user_data, const char *str, uint str_len), void *user_data); void IDP_print(const struct IDProperty *prop); void IDP_BlendWrite(struct BlendWriter *writer, const struct IDProperty *prop); void IDP_BlendReadData_impl(struct BlendDataReader *reader, - IDProperty **prop, + struct IDProperty **prop, const char *caller_func_id); #define IDP_BlendDataRead(reader, prop) IDP_BlendReadData_impl(reader, prop, __func__) -void IDP_BlendReadLib(struct BlendLibReader *reader, IDProperty *prop); -void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop); +void IDP_BlendReadLib(struct BlendLibReader *reader, struct IDProperty *prop); +void IDP_BlendReadExpand(struct BlendExpander *expander, struct IDProperty *prop); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_ipo.h b/source/blender/blenkernel/BKE_ipo.h index 40b9f738bfd..f4871c83caf 100644 --- a/source/blender/blenkernel/BKE_ipo.h +++ b/source/blender/blenkernel/BKE_ipo.h @@ -26,7 +26,6 @@ extern "C" { #endif -struct Ipo; struct Main; void do_versions_ipos_to_animato(struct Main *main); diff --git a/source/blender/blenkernel/BKE_lattice.h b/source/blender/blenkernel/BKE_lattice.h index f4c1a6fdcb4..02fa8b306d3 100644 --- a/source/blender/blenkernel/BKE_lattice.h +++ b/source/blender/blenkernel/BKE_lattice.h @@ -38,7 +38,6 @@ struct Main; struct Mesh; struct Object; struct Scene; -struct bGPDstroke; void BKE_lattice_resize(struct Lattice *lt, int u, int v, int w, struct Object *ltOb); struct Lattice *BKE_lattice_add(struct Main *bmain, const char *name); diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index e5fab35891c..240d6cb18ec 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -23,7 +23,6 @@ #include "BKE_collection.h" #include "DNA_listBase.h" -#include "DNA_scene_types.h" #ifdef __cplusplus extern "C" { @@ -56,9 +55,9 @@ typedef enum eViewLayerCopyMethod { struct ViewLayer *BKE_view_layer_default_view(const struct Scene *scene); struct ViewLayer *BKE_view_layer_default_render(const struct Scene *scene); struct ViewLayer *BKE_view_layer_find(const struct Scene *scene, const char *layer_name); -struct ViewLayer *BKE_view_layer_add(Scene *scene, +struct ViewLayer *BKE_view_layer_add(struct Scene *scene, const char *name, - ViewLayer *view_layer_source, + struct ViewLayer *view_layer_source, const int type); /* DEPRECATED */ @@ -393,10 +392,11 @@ struct ObjectsInModeParams { void *filter_userdata; }; -Base **BKE_view_layer_array_from_bases_in_mode_params(struct ViewLayer *view_layer, - const struct View3D *v3d, - uint *r_len, - const struct ObjectsInModeParams *params); +struct Base **BKE_view_layer_array_from_bases_in_mode_params( + struct ViewLayer *view_layer, + const struct View3D *v3d, + uint *r_len, + const struct ObjectsInModeParams *params); struct Object **BKE_view_layer_array_from_objects_in_mode_params( struct ViewLayer *view_layer, @@ -452,7 +452,8 @@ void BKE_view_layer_verify_aov(struct RenderEngine *engine, struct Scene *scene, struct ViewLayer *view_layer); bool BKE_view_layer_has_valid_aov(struct ViewLayer *view_layer); -ViewLayer *BKE_view_layer_find_with_aov(struct Scene *scene, struct ViewLayerAOV *view_layer_aov); +struct ViewLayer *BKE_view_layer_find_with_aov(struct Scene *scene, + struct ViewLayerAOV *view_layer_aov); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index e54e2fb4b87..b0971278dc7 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -51,7 +51,6 @@ extern "C" { #endif -struct BlendDataReader; struct BlendWriter; struct GHash; struct ID; @@ -298,6 +297,8 @@ void BKE_id_tag_clear_atomic(struct ID *id, int tag); bool BKE_id_is_in_global_main(struct ID *id); +bool BKE_id_can_be_asset(const struct ID *id); + void BKE_id_ordered_list(struct ListBase *ordered_lb, const struct ListBase *lb); void BKE_id_reorder(const struct ListBase *lb, struct ID *id, struct ID *relative, bool after); diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index bf6b5cbccef..13edabd4cb7 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -72,6 +72,7 @@ void BKE_lib_override_library_dependencies_tag(struct Main *bmain, void BKE_lib_override_library_override_group_tag(struct Main *bmain, struct ID *id_root, const uint tag, + const uint missing_tag, const bool do_create_main_relashionships); bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index fb14e340d8b..a8d75213d39 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -38,8 +38,6 @@ extern "C" { #endif -struct wmWindowManager; - /* BKE_libblock_free, delete are declared in BKE_lib_id.h for convenience. */ /* Also IDRemap->flag. */ diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 807f13efe14..2d8dc852d7c 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -26,7 +26,6 @@ #include "BLI_utildefines.h" struct BLI_Stack; -struct BMEditMesh; struct BMesh; struct BMeshCreateParams; struct BMeshFromMeshParams; @@ -678,7 +677,7 @@ void BKE_mesh_calc_edges_loose(struct Mesh *mesh); void BKE_mesh_calc_edges(struct Mesh *mesh, bool keep_existing_edges, const bool select_new_edges); void BKE_mesh_calc_edges_tessface(struct Mesh *mesh); -/* In DerivedMesh.c */ +/* In DerivedMesh.cc */ void BKE_mesh_wrapper_deferred_finalize(struct Mesh *me_eval, const CustomData_MeshMasks *cd_mask_finalize); diff --git a/source/blender/blenkernel/BKE_mesh_fair.h b/source/blender/blenkernel/BKE_mesh_fair.h new file mode 100644 index 00000000000..2d5c85d4129 --- /dev/null +++ b/source/blender/blenkernel/BKE_mesh_fair.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing": + * https://github.com/fedackb/mesh-fairing. + */ + +#pragma once + +/** \file + * \ingroup bke + */ + +#include "BLI_utildefines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Mesh Fairing. */ +/* Creates a smooth as possible geometry patch in a defined area. Different values of depth allow + * to minimize changes in the vertex positions or tangency in the affected area. */ + +typedef enum eMeshFairingDepth { + MESH_FAIRING_DEPTH_POSITION = 1, + MESH_FAIRING_DEPTH_TANGENCY = 2, +} eMeshFairingDepth; + +/* affect_vertices is used to define the fairing area. Indexed by vertex index, set to true when + * the vertex should be modified by fairing. */ +void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm, + bool *affect_vertices, + const eMeshFairingDepth depth); + +/* This function can optionally use the MVert coordinates of deform_mverts to read and write the + * fairing result. When NULL, the function will use mesh->mverts directly. */ +void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh, + struct MVert *deform_mverts, + bool *affect_vertices, + const eMeshFairingDepth depth); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_mesh_runtime.h b/source/blender/blenkernel/BKE_mesh_runtime.h index 87b55c581a2..67c87e96aff 100644 --- a/source/blender/blenkernel/BKE_mesh_runtime.h +++ b/source/blender/blenkernel/BKE_mesh_runtime.h @@ -57,9 +57,9 @@ void BKE_mesh_runtime_verttri_from_looptri(struct MVertTri *r_verttri, const struct MLoopTri *looptri, int looptri_num); -/* NOTE: the functions below are defined in DerivedMesh.c, and are intended to be moved +/* NOTE: the functions below are defined in DerivedMesh.cc, and are intended to be moved * to a more suitable location when that file is removed. - * They should also be renamed to use conventions from BKE, not old DerivedMesh.c. + * They should also be renamed to use conventions from BKE, not old DerivedMesh.cc. * For now keep the names similar to avoid confusion. */ struct Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 73e33124b43..685a8ed98e2 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -35,6 +35,7 @@ struct BlendWriter; struct CustomData_MeshMasks; struct DepsNodeHandle; struct Depsgraph; +struct GeometrySet; struct ID; struct ListBase; struct Main; @@ -43,7 +44,6 @@ struct ModifierData; struct Object; struct Scene; struct bArmature; -struct GeometrySet; typedef enum { /* Should not be used, only for None modifier type */ @@ -244,12 +244,20 @@ typedef struct ModifierTypeInfo { struct Mesh *(*modifyMesh)(struct ModifierData *md, const struct ModifierEvalContext *ctx, struct Mesh *mesh); + struct Hair *(*modifyHair)(struct ModifierData *md, const struct ModifierEvalContext *ctx, struct Hair *hair); - void (*modifyPointCloud)(struct ModifierData *md, - const struct ModifierEvalContext *ctx, - struct GeometrySet *geometry_set); + + /** + * The modifier has to change the geometry set in-place. The geometry set can contain zero or + * more geometry components. This callback can be used by modifiers that don't work on any + * specific type of geometry (e.g. mesh). + */ + void (*modifyGeometrySet)(struct ModifierData *md, + const struct ModifierEvalContext *ctx, + struct GeometrySet *geometry_set); + struct Volume *(*modifyVolume)(struct ModifierData *md, const struct ModifierEvalContext *ctx, struct Volume *volume); diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index 5a668532033..fce25abba7f 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -23,14 +23,13 @@ * \ingroup bke */ +#include "BKE_subsurf.h" #include "BLI_compiler_compat.h" #ifdef __cplusplus extern "C" { #endif -enum MultiresModifiedFlags; - struct Depsgraph; struct DerivedMesh; struct MDisps; diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a8a94958772..5f69fc397e8 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -451,7 +451,7 @@ bool ntreeHasType(const struct bNodeTree *ntree, int type); bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup); void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree); void ntreeUpdateAllNew(struct Main *main); -void ntreeUpdateAllUsers(struct Main *main, struct bNodeTree *ngroup); +void ntreeUpdateAllUsers(struct Main *main, struct ID *id); void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***deplist, int *totnodes); @@ -1347,9 +1347,12 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_POINT_INSTANCE 1005 #define GEO_NODE_SUBDIVISION_SURFACE 1006 #define GEO_NODE_OBJECT_INFO 1007 -#define GEO_NODE_RANDOM_ATTRIBUTE 1008 +#define GEO_NODE_ATTRIBUTE_RANDOMIZE 1008 #define GEO_NODE_ATTRIBUTE_MATH 1009 #define GEO_NODE_JOIN_GEOMETRY 1010 +#define GEO_NODE_ATTRIBUTE_FILL 1011 +#define GEO_NODE_ATTRIBUTE_MIX 1012 +#define GEO_NODE_ATTRIBUTE_COLOR_RAMP 1013 /** \} */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 479bc56b2dc..aaed2649ad9 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -25,6 +25,7 @@ #include "BLI_bitmap.h" #include "BLI_utildefines.h" +#include "DNA_brush_enums.h" #include "DNA_object_enums.h" #ifdef __cplusplus @@ -33,6 +34,9 @@ extern "C" { struct BMFace; struct BMesh; +struct BlendDataReader; +struct BlendLibReader; +struct BlendWriter; struct Brush; struct CurveMapping; struct Depsgraph; @@ -54,7 +58,6 @@ struct Paint; struct PaintCurve; struct Palette; struct PaletteColor; -struct ReportList; struct Scene; struct StrokeCache; struct SubdivCCG; @@ -66,11 +69,6 @@ struct ViewLayer; struct bContext; struct bToolRef; struct tPaletteColorHSV; -struct BlendWriter; -struct BlendDataReader; -struct BlendLibReader; - -enum eOverlayFlags; extern const char PAINT_CURSOR_SCULPT[3]; extern const char PAINT_CURSOR_VERTEX_PAINT[3]; diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 73815325594..3913ede9049 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -29,11 +29,8 @@ #include "BLI_buffer.h" #include "BLI_utildefines.h" -#include "DNA_object_types.h" #include "DNA_particle_types.h" -#include "BKE_customdata.h" - #ifdef __cplusplus extern "C" { #endif @@ -45,9 +42,9 @@ struct ParticleSystemModifierData; struct BVHTreeRay; struct BVHTreeRayHit; -struct BlendWriter; struct BlendDataReader; struct BlendLibReader; +struct BlendWriter; struct CustomData_MeshMasks; struct Depsgraph; struct EdgeHash; @@ -302,7 +299,7 @@ int psys_get_tot_child(struct Scene *scene, struct ParticleSystem *psys_get_current(struct Object *ob); /* for rna */ short psys_get_current_num(struct Object *ob); -void psys_set_current_num(Object *ob, int index); +void psys_set_current_num(struct Object *ob, int index); /* UNUSED */ // struct Object *psys_find_object(struct Scene *scene, struct ParticleSystem *psys); diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index fd600a41796..0fa44067b16 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -48,7 +48,6 @@ struct PBVH; struct PBVHNode; struct SubdivCCG; struct TaskParallelSettings; -struct TaskParallelTLS; typedef struct PBVH PBVH; typedef struct PBVHNode PBVHNode; diff --git a/source/blender/blenkernel/BKE_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh index 42a853886d7..bbee09c7bf4 100644 --- a/source/blender/blenkernel/BKE_persistent_data_handle.hh +++ b/source/blender/blenkernel/BKE_persistent_data_handle.hh @@ -27,6 +27,7 @@ #include "DNA_ID.h" +struct Collection; struct Object; namespace blender::bke { @@ -82,6 +83,11 @@ class PersistentObjectHandle : public PersistentIDHandle { using PersistentIDHandle::PersistentIDHandle; }; +class PersistentCollectionHandle : public PersistentIDHandle { + friend PersistentDataHandleMap; + using PersistentIDHandle::PersistentIDHandle; +}; + class PersistentDataHandleMap { private: Map<int32_t, ID *> id_by_handle_; @@ -107,6 +113,12 @@ class PersistentDataHandleMap { return PersistentObjectHandle(handle); } + PersistentCollectionHandle lookup(Collection *collection) const + { + const int handle = handle_by_id_.lookup_default((ID *)collection, -1); + return PersistentCollectionHandle(handle); + } + ID *lookup(const PersistentIDHandle &handle) const { ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr); @@ -124,6 +136,18 @@ class PersistentDataHandleMap { } return (Object *)id; } + + Collection *lookup(const PersistentCollectionHandle &handle) const + { + ID *id = this->lookup((const PersistentIDHandle &)handle); + if (id == nullptr) { + return nullptr; + } + if (GS(id->name) != ID_GR) { + return nullptr; + } + return (Collection *)id; + } }; } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index f2edebededc..170eb4ba662 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -23,12 +23,10 @@ * \ingroup bke */ -#include "DNA_ID.h" -#include "DNA_boid_types.h" -#include "DNA_dynamicpaint_types.h" -#include "DNA_object_force_types.h" -#include "DNA_pointcache_types.h" -#include <stdio.h> /* for FILE */ +#include "DNA_boid_types.h" /* for #BoidData */ +#include "DNA_pointcache_types.h" /* for #BPHYS_TOT_DATA */ + +#include <stdio.h> /* for #FILE */ #ifdef __cplusplus extern "C" { @@ -79,7 +77,10 @@ extern "C" { #define PTCACHE_READ_OLD 3 /* Structs */ +struct BlendDataReader; +struct BlendWriter; struct ClothModifierData; +struct DynamicPaintSurface; struct FluidModifierData; struct ListBase; struct Main; @@ -91,8 +92,6 @@ struct RigidBodyWorld; struct Scene; struct SoftBody; struct ViewLayer; -struct BlendWriter; -struct BlendDataReader; /* temp structure for read/write */ typedef struct PTCacheData { diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h new file mode 100644 index 00000000000..04a41d425bb --- /dev/null +++ b/source/blender/blenkernel/BKE_preferences.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bke + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "BLI_compiler_attrs.h" + +struct UserDef; +struct bUserAssetLibrary; + +void BKE_preferences_asset_library_free(struct bUserAssetLibrary *library) ATTR_NONNULL(); + +struct bUserAssetLibrary *BKE_preferences_asset_library_add(struct UserDef *userdef, + const char *name, + const char *path) ATTR_NONNULL(1); +void BKE_preferences_asset_library_name_set(struct UserDef *userdef, + struct bUserAssetLibrary *library, + const char *name) ATTR_NONNULL(); + +void BKE_preferences_asset_library_remove(struct UserDef *userdef, + struct bUserAssetLibrary *library) ATTR_NONNULL(); + +struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_index( + const struct UserDef *userdef, int index) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_name( + const struct UserDef *userdef, const char *name) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +int BKE_preferences_asset_library_get_index(const struct UserDef *userdef, + const struct bUserAssetLibrary *library) + ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; + +void BKE_preferences_asset_library_default_add(struct UserDef *userdef) ATTR_NONNULL(); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 473a684eaba..7b5df98d148 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -51,7 +51,6 @@ struct View3D; struct View3DShading; struct WorkSpace; struct bContext; -struct bContextDataResult; struct bScreen; struct uiLayout; struct uiList; diff --git a/source/blender/blenkernel/BKE_sequencer_offscreen.h b/source/blender/blenkernel/BKE_sequencer_offscreen.h index f5f6067b06e..25a78fcfbad 100644 --- a/source/blender/blenkernel/BKE_sequencer_offscreen.h +++ b/source/blender/blenkernel/BKE_sequencer_offscreen.h @@ -23,26 +23,23 @@ * \ingroup bke */ -#include "DNA_object_enums.h" - -#include "DNA_view3d_types.h" - -#include "IMB_imbuf_types.h" - #ifdef __cplusplus extern "C" { #endif struct GPUOffScreen; +enum eDrawType; +enum eImBufFlags; + typedef struct ImBuf *(*SequencerDrawView)(struct Depsgraph *depsgraph, struct Scene *scene, struct View3DShading *shading_override, - eDrawType drawtype, + enum eDrawType drawtype, struct Object *camera, int width, int height, - eImBufFlags flag, + enum eImBufFlags flag, eV3DOffscreenDrawFlag draw_flags, int alpha_mode, const char *viewname, diff --git a/source/blender/blenkernel/BKE_shader_fx.h b/source/blender/blenkernel/BKE_shader_fx.h index 7c2d363be5c..23bd62c70bc 100644 --- a/source/blender/blenkernel/BKE_shader_fx.h +++ b/source/blender/blenkernel/BKE_shader_fx.h @@ -19,7 +19,6 @@ * \ingroup bke */ -#include "BKE_customdata.h" #include "BLI_compiler_attrs.h" #include "DNA_shader_fx_types.h" /* needed for all enum typdefs */ @@ -28,14 +27,14 @@ extern "C" { #endif struct ARegionType; +struct BlendDataReader; +struct BlendLibReader; +struct BlendWriter; struct ID; struct ListBase; struct ModifierUpdateDepsgraphContext; struct Object; struct ShaderFxData; -struct BlendWriter; -struct BlendDataReader; -struct BlendLibReader; #define SHADER_FX_ACTIVE(_fx, _is_render) \ ((((_fx)->mode & eShaderFxMode_Realtime) && (_is_render == false)) || \ diff --git a/source/blender/blenkernel/BKE_simulation.h b/source/blender/blenkernel/BKE_simulation.h index 23735990079..37372036130 100644 --- a/source/blender/blenkernel/BKE_simulation.h +++ b/source/blender/blenkernel/BKE_simulation.h @@ -16,8 +16,6 @@ #pragma once -#include "DNA_simulation_types.h" - #ifdef __cplusplus extern "C" { #endif @@ -25,6 +23,7 @@ extern "C" { struct Depsgraph; struct Main; struct Scene; +struct Simulation; void *BKE_simulation_add(struct Main *bmain, const char *name); diff --git a/source/blender/blenkernel/BKE_speaker.h b/source/blender/blenkernel/BKE_speaker.h index e288c9f3eb4..9defa887d3c 100644 --- a/source/blender/blenkernel/BKE_speaker.h +++ b/source/blender/blenkernel/BKE_speaker.h @@ -26,7 +26,6 @@ extern "C" { #endif struct Main; -struct Speaker; void *BKE_speaker_add(struct Main *bmain, const char *name); diff --git a/source/blender/blenkernel/BKE_studiolight.h b/source/blender/blenkernel/BKE_studiolight.h index 614523be123..70b8743bcd2 100644 --- a/source/blender/blenkernel/BKE_studiolight.h +++ b/source/blender/blenkernel/BKE_studiolight.h @@ -29,7 +29,7 @@ #include "BLI_path_util.h" -#include "DNA_userdef_types.h" +#include "DNA_userdef_types.h" /* for #SolidLight */ #ifdef __cplusplus extern "C" { diff --git a/source/blender/blenkernel/BKE_text_suggestions.h b/source/blender/blenkernel/BKE_text_suggestions.h index f54e45b6c2f..7561e1d1d08 100644 --- a/source/blender/blenkernel/BKE_text_suggestions.h +++ b/source/blender/blenkernel/BKE_text_suggestions.h @@ -22,8 +22,6 @@ * \ingroup bke */ -#include "DNA_text_types.h" - #ifdef __cplusplus extern "C" { #endif @@ -62,9 +60,9 @@ typedef struct SuggList { void free_texttools(void); /* Used to identify which Text object the current tools should appear against */ -void texttool_text_set_active(Text *text); +void texttool_text_set_active(struct Text *text); void texttool_text_clear(void); -short texttool_text_is_active(Text *text); +short texttool_text_is_active(struct Text *text); /* Suggestions */ void texttool_suggest_add(const char *name, char type); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index c962f0a6a8c..ae0a180311c 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -36,12 +36,13 @@ set(INC ../makesrna ../modifiers ../nodes + ../render ../sequencer ../shader_fx ../simulation - ../render ../../../intern/ghost ../../../intern/glew-mx + ../../../intern/eigen ../../../intern/guardedalloc ../../../intern/iksolver/extern ../../../intern/atomic @@ -66,7 +67,7 @@ set(SRC intern/CCGSubSurf.c intern/CCGSubSurf_legacy.c intern/CCGSubSurf_util.c - intern/DerivedMesh.c + intern/DerivedMesh.cc intern/action.c intern/addon.c intern/anim_data.c @@ -77,6 +78,7 @@ set(SRC intern/armature.c intern/armature_deform.c intern/armature_update.c + intern/asset.c intern/attribute.c intern/attribute_access.cc intern/autoexec.c @@ -101,7 +103,7 @@ set(SRC intern/constraint.c intern/context.c intern/crazyspace.c - intern/cryptomatte.c + intern/cryptomatte.cc intern/curve.c intern/curve_bevel.c intern/curve_decimate.c @@ -132,7 +134,7 @@ set(SRC intern/gpencil_geom.c intern/gpencil_modifier.c intern/hair.c - intern/icons.c + intern/icons.cc intern/icons_rasterize.c intern/idprop.c intern/idprop_utils.c @@ -169,6 +171,7 @@ set(SRC intern/mesh.c intern/mesh_convert.c intern/mesh_evaluate.c + intern/mesh_fair.cc intern/mesh_iterators.c intern/mesh_mapping.c intern/mesh_merge.c @@ -214,6 +217,7 @@ set(SRC intern/pbvh_bmesh.c intern/pointcache.c intern/pointcloud.cc + intern/preferences.c intern/report.c intern/rigidbody.c intern/scene.c @@ -271,6 +275,7 @@ set(SRC BKE_attribute.h BKE_attribute_access.hh BKE_autoexec.h + BKE_asset.h BKE_blender.h BKE_blender_copybuffer.h BKE_blender_undo.h @@ -350,6 +355,7 @@ set(SRC BKE_mball_tessellate.h BKE_mesh.h BKE_mesh_iterators.h + BKE_mesh_fair.h BKE_mesh_mapping.h BKE_mesh_mirror.h BKE_mesh_remap.h @@ -374,6 +380,7 @@ set(SRC BKE_persistent_data_handle.hh BKE_pointcache.h BKE_pointcloud.h + BKE_preferences.h BKE_report.h BKE_rigidbody.h BKE_scene.h @@ -726,8 +733,8 @@ if(WITH_GTESTS) intern/armature_test.cc intern/fcurve_test.cc intern/lattice_deform_test.cc - intern/tracking_test.cc intern/layer_test.cc + intern/tracking_test.cc ) set(TEST_INC ../editors/include diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.cc index eeff04788f9..b8219dcf7ac 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -21,8 +21,8 @@ * \ingroup bke */ -#include <limits.h> -#include <string.h> +#include <climits> +#include <cstring> #include "MEM_guardedalloc.h" @@ -38,16 +38,19 @@ #include "BLI_array.h" #include "BLI_bitmap.h" #include "BLI_blenlib.h" +#include "BLI_float2.hh" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_task.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_DerivedMesh.h" #include "BKE_bvhutils.h" #include "BKE_colorband.h" #include "BKE_deform.h" #include "BKE_editmesh.h" +#include "BKE_geometry_set.hh" #include "BKE_key.h" #include "BKE_layer.h" #include "BKE_lib_id.h" @@ -81,7 +84,7 @@ #ifdef USE_MODIFIER_VALIDATE # define ASSERT_IS_VALID_MESH(mesh) \ - (BLI_assert((mesh == NULL) || (BKE_mesh_is_valid(mesh) == true))) + (BLI_assert((mesh == nullptr) || (BKE_mesh_is_valid(mesh) == true))) #else # define ASSERT_IS_VALID_MESH(mesh) #endif @@ -96,10 +99,11 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, static MVert *dm_getVertArray(DerivedMesh *dm) { - MVert *mvert = CustomData_get_layer(&dm->vertData, CD_MVERT); + MVert *mvert = (MVert *)CustomData_get_layer(&dm->vertData, CD_MVERT); if (!mvert) { - mvert = CustomData_add_layer(&dm->vertData, CD_MVERT, CD_CALLOC, NULL, dm->getNumVerts(dm)); + mvert = (MVert *)CustomData_add_layer( + &dm->vertData, CD_MVERT, CD_CALLOC, nullptr, dm->getNumVerts(dm)); CustomData_set_layer_flag(&dm->vertData, CD_MVERT, CD_FLAG_TEMPORARY); dm->copyVertArray(dm, mvert); } @@ -109,10 +113,11 @@ static MVert *dm_getVertArray(DerivedMesh *dm) static MEdge *dm_getEdgeArray(DerivedMesh *dm) { - MEdge *medge = CustomData_get_layer(&dm->edgeData, CD_MEDGE); + MEdge *medge = (MEdge *)CustomData_get_layer(&dm->edgeData, CD_MEDGE); if (!medge) { - medge = CustomData_add_layer(&dm->edgeData, CD_MEDGE, CD_CALLOC, NULL, dm->getNumEdges(dm)); + medge = (MEdge *)CustomData_add_layer( + &dm->edgeData, CD_MEDGE, CD_CALLOC, nullptr, dm->getNumEdges(dm)); CustomData_set_layer_flag(&dm->edgeData, CD_MEDGE, CD_FLAG_TEMPORARY); dm->copyEdgeArray(dm, medge); } @@ -122,7 +127,7 @@ static MEdge *dm_getEdgeArray(DerivedMesh *dm) static MFace *dm_getTessFaceArray(DerivedMesh *dm) { - MFace *mface = CustomData_get_layer(&dm->faceData, CD_MFACE); + MFace *mface = (MFace *)CustomData_get_layer(&dm->faceData, CD_MFACE); if (!mface) { int numTessFaces = dm->getNumTessFaces(dm); @@ -132,10 +137,11 @@ static MFace *dm_getTessFaceArray(DerivedMesh *dm) * this layer is needed with non-zero size, but currently CD stuff does not check * for requested layer size on creation and just returns layer which was previously * added (sergey) */ - return NULL; + return nullptr; } - mface = CustomData_add_layer(&dm->faceData, CD_MFACE, CD_CALLOC, NULL, numTessFaces); + mface = (MFace *)CustomData_add_layer( + &dm->faceData, CD_MFACE, CD_CALLOC, nullptr, numTessFaces); CustomData_set_layer_flag(&dm->faceData, CD_MFACE, CD_FLAG_TEMPORARY); dm->copyTessFaceArray(dm, mface); } @@ -145,10 +151,11 @@ static MFace *dm_getTessFaceArray(DerivedMesh *dm) static MLoop *dm_getLoopArray(DerivedMesh *dm) { - MLoop *mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP); + MLoop *mloop = (MLoop *)CustomData_get_layer(&dm->loopData, CD_MLOOP); if (!mloop) { - mloop = CustomData_add_layer(&dm->loopData, CD_MLOOP, CD_CALLOC, NULL, dm->getNumLoops(dm)); + mloop = (MLoop *)CustomData_add_layer( + &dm->loopData, CD_MLOOP, CD_CALLOC, nullptr, dm->getNumLoops(dm)); CustomData_set_layer_flag(&dm->loopData, CD_MLOOP, CD_FLAG_TEMPORARY); dm->copyLoopArray(dm, mloop); } @@ -158,10 +165,11 @@ static MLoop *dm_getLoopArray(DerivedMesh *dm) static MPoly *dm_getPolyArray(DerivedMesh *dm) { - MPoly *mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); + MPoly *mpoly = (MPoly *)CustomData_get_layer(&dm->polyData, CD_MPOLY); if (!mpoly) { - mpoly = CustomData_add_layer(&dm->polyData, CD_MPOLY, CD_CALLOC, NULL, dm->getNumPolys(dm)); + mpoly = (MPoly *)CustomData_add_layer( + &dm->polyData, CD_MPOLY, CD_CALLOC, nullptr, dm->getNumPolys(dm)); CustomData_set_layer_flag(&dm->polyData, CD_MPOLY, CD_FLAG_TEMPORARY); dm->copyPolyArray(dm, mpoly); } @@ -171,7 +179,8 @@ static MPoly *dm_getPolyArray(DerivedMesh *dm) static MVert *dm_dupVertArray(DerivedMesh *dm) { - MVert *tmp = MEM_malloc_arrayN(dm->getNumVerts(dm), sizeof(*tmp), "dm_dupVertArray tmp"); + MVert *tmp = (MVert *)MEM_malloc_arrayN( + dm->getNumVerts(dm), sizeof(*tmp), "dm_dupVertArray tmp"); if (tmp) { dm->copyVertArray(dm, tmp); @@ -182,7 +191,8 @@ static MVert *dm_dupVertArray(DerivedMesh *dm) static MEdge *dm_dupEdgeArray(DerivedMesh *dm) { - MEdge *tmp = MEM_malloc_arrayN(dm->getNumEdges(dm), sizeof(*tmp), "dm_dupEdgeArray tmp"); + MEdge *tmp = (MEdge *)MEM_malloc_arrayN( + dm->getNumEdges(dm), sizeof(*tmp), "dm_dupEdgeArray tmp"); if (tmp) { dm->copyEdgeArray(dm, tmp); @@ -193,7 +203,8 @@ static MEdge *dm_dupEdgeArray(DerivedMesh *dm) static MFace *dm_dupFaceArray(DerivedMesh *dm) { - MFace *tmp = MEM_malloc_arrayN(dm->getNumTessFaces(dm), sizeof(*tmp), "dm_dupFaceArray tmp"); + MFace *tmp = (MFace *)MEM_malloc_arrayN( + dm->getNumTessFaces(dm), sizeof(*tmp), "dm_dupFaceArray tmp"); if (tmp) { dm->copyTessFaceArray(dm, tmp); @@ -204,7 +215,8 @@ static MFace *dm_dupFaceArray(DerivedMesh *dm) static MLoop *dm_dupLoopArray(DerivedMesh *dm) { - MLoop *tmp = MEM_malloc_arrayN(dm->getNumLoops(dm), sizeof(*tmp), "dm_dupLoopArray tmp"); + MLoop *tmp = (MLoop *)MEM_malloc_arrayN( + dm->getNumLoops(dm), sizeof(*tmp), "dm_dupLoopArray tmp"); if (tmp) { dm->copyLoopArray(dm, tmp); @@ -215,7 +227,8 @@ static MLoop *dm_dupLoopArray(DerivedMesh *dm) static MPoly *dm_dupPolyArray(DerivedMesh *dm) { - MPoly *tmp = MEM_malloc_arrayN(dm->getNumPolys(dm), sizeof(*tmp), "dm_dupPolyArray tmp"); + MPoly *tmp = (MPoly *)MEM_malloc_arrayN( + dm->getNumPolys(dm), sizeof(*tmp), "dm_dupPolyArray tmp"); if (tmp) { dm->copyPolyArray(dm, tmp); @@ -239,14 +252,14 @@ static const MLoopTri *dm_getLoopTriArray(DerivedMesh *dm) looptri = dm->looptris.array; BLI_rw_mutex_unlock(&loops_cache_lock); - if (looptri != NULL) { + if (looptri != nullptr) { BLI_assert(dm->getNumLoopTri(dm) == dm->looptris.num); } else { BLI_rw_mutex_lock(&loops_cache_lock, THREAD_LOCK_WRITE); - /* We need to ensure array is still NULL inside mutex-protected code, + /* We need to ensure array is still nullptr inside mutex-protected code, * some other thread might have already recomputed those looptris. */ - if (dm->looptris.array == NULL) { + if (dm->looptris.array == nullptr) { dm->recalcLoopTri(dm); } looptri = dm->looptris.array; @@ -343,7 +356,7 @@ void DM_init(DerivedMesh *dm, DM_init_funcs(dm); dm->needsFree = 1; - dm->dirty = 0; + dm->dirty = (DMDirtyFlag)0; /* don't use CustomData_reset(...); because we dont want to touch customdata */ copy_vn_i(dm->vertData.typemap, CD_NUMTYPES, -1); @@ -385,7 +398,7 @@ void DM_from_template_ex(DerivedMesh *dm, DM_init_funcs(dm); dm->needsFree = 1; - dm->dirty = 0; + dm->dirty = (DMDirtyFlag)0; } void DM_from_template(DerivedMesh *dm, DerivedMesh *source, @@ -482,7 +495,7 @@ void DM_ensure_looptri_data(DerivedMesh *dm) const unsigned int totloop = dm->numLoopData; const int looptris_num = poly_to_tri_count(totpoly, totloop); - BLI_assert(dm->looptris.array_wip == NULL); + BLI_assert(dm->looptris.array_wip == nullptr); SWAP(MLoopTri *, dm->looptris.array, dm->looptris.array_wip); @@ -494,8 +507,8 @@ void DM_ensure_looptri_data(DerivedMesh *dm) } if (totpoly) { - if (dm->looptris.array_wip == NULL) { - dm->looptris.array_wip = MEM_malloc_arrayN( + if (dm->looptris.array_wip == nullptr) { + dm->looptris.array_wip = (MLoopTri *)MEM_malloc_arrayN( looptris_num, sizeof(*dm->looptris.array_wip), __func__); dm->looptris.num_alloc = looptris_num; } @@ -656,7 +669,7 @@ void DM_interp_vert_data(DerivedMesh *source, int dest_index) { CustomData_interp( - &source->vertData, &dest->vertData, src_indices, weights, NULL, count, dest_index); + &source->vertData, &dest->vertData, src_indices, weights, nullptr, count, dest_index); } static float (*get_editbmesh_orco_verts(BMEditMesh *em))[3] @@ -669,7 +682,7 @@ static float (*get_editbmesh_orco_verts(BMEditMesh *em))[3] /* these may not really be the orco's, but it's only for preview. * could be solver better once, but isn't simple */ - orco = MEM_malloc_arrayN(em->bm->totvert, sizeof(float[3]), "BMEditMesh Orco"); + orco = (float(*)[3])MEM_malloc_arrayN(em->bm->totvert, sizeof(float[3]), "BMEditMesh Orco"); BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) { copy_v3_v3(orco[i], eve->co); @@ -702,14 +715,14 @@ static float (*get_orco_coords(Object *ob, BMEditMesh *em, int layer, int *free) clmd->sim_parms->shapekey_rest); if (kb && kb->data) { - return kb->data; + return (float(*)[3])kb->data; } } - return NULL; + return nullptr; } - return NULL; + return nullptr; } static Mesh *create_orco_mesh(Object *ob, Mesh *me, BMEditMesh *em, int layer) @@ -719,7 +732,7 @@ static Mesh *create_orco_mesh(Object *ob, Mesh *me, BMEditMesh *em, int layer) int free; if (em) { - mesh = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, me); + mesh = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, me); } else { mesh = BKE_mesh_copy_for_eval(me, true); @@ -748,10 +761,10 @@ static void add_orco_mesh(Object *ob, BMEditMesh *em, Mesh *mesh, Mesh *mesh_orc free = 1; if (mesh_orco->totvert == totvert) { - orco = BKE_mesh_vert_coords_alloc(mesh_orco, NULL); + orco = BKE_mesh_vert_coords_alloc(mesh_orco, nullptr); } else { - orco = BKE_mesh_vert_coords_alloc(mesh, NULL); + orco = BKE_mesh_vert_coords_alloc(mesh, nullptr); } } else { @@ -762,14 +775,14 @@ static void add_orco_mesh(Object *ob, BMEditMesh *em, Mesh *mesh, Mesh *mesh_orc if (orco) { if (layer == CD_ORCO) { - BKE_mesh_orco_verts_transform(ob->data, orco, totvert, 0); + BKE_mesh_orco_verts_transform((Mesh *)ob->data, orco, totvert, 0); } - if (!(layerorco = CustomData_get_layer(&mesh->vdata, layer))) { - CustomData_add_layer(&mesh->vdata, layer, CD_CALLOC, NULL, mesh->totvert); + if (!(layerorco = (float(*)[3])CustomData_get_layer(&mesh->vdata, layer))) { + CustomData_add_layer(&mesh->vdata, layer, CD_CALLOC, nullptr, mesh->totvert); BKE_mesh_update_customdata_pointers(mesh, false); - layerorco = CustomData_get_layer(&mesh->vdata, layer); + layerorco = (float(*)[3])CustomData_get_layer(&mesh->vdata, layer); } memcpy(layerorco, orco, sizeof(float[3]) * totvert); @@ -797,10 +810,10 @@ static void mesh_calc_modifier_final_normals(const Mesh *mesh_input, * (BKE_mesh_calc_normals_split() assumes that if that data exists, it is always valid). */ if (do_poly_normals) { if (!CustomData_has_layer(&mesh_final->pdata, CD_NORMAL)) { - float(*polynors)[3] = CustomData_add_layer( - &mesh_final->pdata, CD_NORMAL, CD_CALLOC, NULL, mesh_final->totpoly); + float(*polynors)[3] = (float(*)[3])CustomData_add_layer( + &mesh_final->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh_final->totpoly); BKE_mesh_calc_normals_poly(mesh_final->mvert, - NULL, + nullptr, mesh_final->totvert, mesh_final->mloop, mesh_final->mpoly, @@ -869,6 +882,56 @@ void BKE_mesh_wrapper_deferred_finalize(Mesh *me_eval, BLI_assert(me_eval->runtime.wrapper_type_finalize == 0); } +/** + * Modifies the given mesh and geometry set. The geometry set is expect to have NO mesh component. + * After this function ends, the geometry set will still have NO mesh component. Instead, an input + * mesh is passed separately and is returned separately. + * + * The purpose of the geometry set is to store all non-mesh geometry components that are generated + * by modifiers. + */ +static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md, + const ModifierEvalContext &mectx, + Object *ob, + Mesh *input_mesh, + GeometrySet &geometry_set) +{ + Mesh *mesh_output = nullptr; + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); + if (mti->modifyGeometrySet == nullptr) { + mesh_output = BKE_modifier_modify_mesh(md, &mectx, input_mesh); + } + else { + /* For performance reasons, this should be called by the modifier and/or nodes themselves at + * some point. */ + BKE_mesh_wrapper_ensure_mdata(input_mesh); + + /* Adds a new mesh component to the geometry set based on the #input_mesh. */ + BLI_assert(!geometry_set.has<MeshComponent>()); + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_component.replace(input_mesh, GeometryOwnershipType::Editable); + mesh_component.copy_vertex_group_names_from_object(*ob); + + /* Let the modifier change the geometry set. */ + mti->modifyGeometrySet(md, &mectx, &geometry_set); + + /* Release the mesh from the geometry set again. */ + if (geometry_set.has<MeshComponent>()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_output = mesh_component.release(); + geometry_set.remove<MeshComponent>(); + } + + /* Return an empty mesh instead of null. */ + if (mesh_output == nullptr) { + mesh_output = BKE_mesh_new_nomain(0, 0, 0, 0, 0); + BKE_mesh_copy_settings(mesh_output, input_mesh); + } + } + + return mesh_output; +} + static void mesh_calc_modifiers(struct Depsgraph *depsgraph, Scene *scene, Object *ob, @@ -880,45 +943,50 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, const bool allow_shared_mesh, /* return args */ Mesh **r_deform, - Mesh **r_final) + Mesh **r_final, + GeometrySet **r_geometry_set) { /* Input and final mesh. Final mesh is only created the moment the first * constructive modifier is executed, or a deform modifier needs normals * or certain data layers. */ - Mesh *mesh_input = ob->data; - Mesh *mesh_final = NULL; - Mesh *mesh_deform = NULL; + Mesh *mesh_input = (Mesh *)ob->data; + Mesh *mesh_final = nullptr; + Mesh *mesh_deform = nullptr; + /* This geometry set contains the non-mesh data that might be generated by modifiers. */ + GeometrySet geometry_set_final; BLI_assert((mesh_input->id.tag & LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT) == 0); /* Deformed vertex locations array. Deform only modifier need this type of * float array rather than MVert*. Tracked along with mesh_final as an * optimization to avoid copying coordinates back and forth if there are * multiple sequential deform only modifiers. */ - float(*deformed_verts)[3] = NULL; + float(*deformed_verts)[3] = nullptr; int num_deformed_verts = mesh_input->totvert; bool isPrevDeform = false; /* Mesh with constructive modifiers but no deformation applied. Tracked * along with final mesh if undeformed / orco coordinates are requested * for texturing. */ - Mesh *mesh_orco = NULL; - Mesh *mesh_orco_cloth = NULL; + Mesh *mesh_orco = nullptr; + Mesh *mesh_orco_cloth = nullptr; /* Modifier evaluation modes. */ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; /* Sculpt can skip certain modifiers. */ - const bool has_multires = BKE_sculpt_multires_active(scene, ob) != NULL; + const bool has_multires = BKE_sculpt_multires_active(scene, ob) != nullptr; bool multires_applied = false; const bool sculpt_mode = ob->mode & OB_MODE_SCULPT && ob->sculpt && !use_render; const bool sculpt_dyntopo = (sculpt_mode && ob->sculpt->bm) && !use_render; /* Modifier evaluation contexts for different types of modifiers. */ - ModifierApplyFlag apply_render = use_render ? MOD_APPLY_RENDER : 0; - ModifierApplyFlag apply_cache = use_cache ? MOD_APPLY_USECACHE : 0; - const ModifierEvalContext mectx = {depsgraph, ob, apply_render | apply_cache}; - const ModifierEvalContext mectx_orco = {depsgraph, ob, apply_render | MOD_APPLY_ORCO}; + ModifierApplyFlag apply_render = use_render ? MOD_APPLY_RENDER : (ModifierApplyFlag)0; + ModifierApplyFlag apply_cache = use_cache ? MOD_APPLY_USECACHE : (ModifierApplyFlag)0; + const ModifierEvalContext mectx = { + depsgraph, ob, (ModifierApplyFlag)(apply_render | apply_cache)}; + const ModifierEvalContext mectx_orco = { + depsgraph, ob, (ModifierApplyFlag)(apply_render | MOD_APPLY_ORCO)}; /* Get effective list of modifiers to execute. Some effects like shape keys * are added as virtual modifiers before the user created modifiers. */ @@ -930,10 +998,10 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, * even if the resulting data is not used in a material. Only in object mode. * TODO: this is broken, not drawn by the drawn manager. */ const bool do_mod_mcol = (ob->mode == OB_MODE_OBJECT); - ModifierData *previewmd = NULL; + ModifierData *previewmd = nullptr; CustomData_MeshMasks previewmask = {0}; if (do_mod_mcol) { - /* Find the last active modifier generating a preview, or NULL if none. */ + /* Find the last active modifier generating a preview, or nullptr if none. */ /* XXX Currently, DPaint modifier just ignores this. * Needs a stupid hack... * The whole "modifier preview" thing has to be (re?)designed, anyway! */ @@ -957,7 +1025,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* Apply all leading deform modifiers. */ if (useDeform) { for (; md; md = md->next, md_datamask = md_datamask->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (!BKE_modifier_is_enabled(scene, md, required_mode)) { continue; @@ -972,7 +1040,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, deformed_verts = BKE_mesh_vert_coords_alloc(mesh_input, &num_deformed_verts); } else if (isPrevDeform && mti->dependsOnNormals && mti->dependsOnNormals(md)) { - if (mesh_final == NULL) { + if (mesh_final == nullptr) { mesh_final = BKE_mesh_copy_for_eval(mesh_input, true); ASSERT_IS_VALID_MESH(mesh_final); } @@ -989,7 +1057,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* grab modifiers until index i */ if ((index != -1) && (BLI_findindex(&ob->modifiers, md) >= index)) { - md = NULL; + md = nullptr; break; } } @@ -1009,7 +1077,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* Apply all remaining constructive and deforming modifiers. */ bool have_non_onlydeform_modifiers_appled = false; for (; md; md = md->next, md_datamask = md_datamask->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (!BKE_modifier_is_enabled(scene, md, required_mode)) { continue; @@ -1069,7 +1137,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, CustomData_MeshMasks mask = {0}; mti->requiredDataMask(ob, md, &mask); if (mask.vmask & CD_MASK_ORCO) { - add_orco_mesh(ob, NULL, mesh_final, mesh_orco, CD_ORCO); + add_orco_mesh(ob, nullptr, mesh_final, mesh_orco, CD_ORCO); } } @@ -1092,7 +1160,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* if this is not the last modifier in the stack then recalculate the normals * to avoid giving bogus normals to the next modifier see: T23673. */ else if (isPrevDeform && mti->dependsOnNormals && mti->dependsOnNormals(md)) { - if (mesh_final == NULL) { + if (mesh_final == nullptr) { mesh_final = BKE_mesh_copy_for_eval(mesh_input, true); ASSERT_IS_VALID_MESH(mesh_final); } @@ -1103,7 +1171,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, else { bool check_for_needs_mapping = false; /* apply vertex coordinates or build a Mesh as necessary */ - if (mesh_final != NULL) { + if (mesh_final != nullptr) { if (have_non_onlydeform_modifiers_appled == false) { /* If we only deformed, we won't have initialized #CD_ORIGINDEX. * as this is the only part of the function that initializes mapping. */ @@ -1137,20 +1205,23 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, ((nextmask.vmask | nextmask.emask | nextmask.pmask) & CD_MASK_ORIGINDEX)) { /* calc */ CustomData_add_layer( - &mesh_final->vdata, CD_ORIGINDEX, CD_CALLOC, NULL, mesh_final->totvert); + &mesh_final->vdata, CD_ORIGINDEX, CD_CALLOC, nullptr, mesh_final->totvert); CustomData_add_layer( - &mesh_final->edata, CD_ORIGINDEX, CD_CALLOC, NULL, mesh_final->totedge); + &mesh_final->edata, CD_ORIGINDEX, CD_CALLOC, nullptr, mesh_final->totedge); CustomData_add_layer( - &mesh_final->pdata, CD_ORIGINDEX, CD_CALLOC, NULL, mesh_final->totpoly); + &mesh_final->pdata, CD_ORIGINDEX, CD_CALLOC, nullptr, mesh_final->totpoly); /* Not worth parallelizing this, * gives less than 0.1% overall speedup in best of best cases... */ - range_vn_i( - CustomData_get_layer(&mesh_final->vdata, CD_ORIGINDEX), mesh_final->totvert, 0); - range_vn_i( - CustomData_get_layer(&mesh_final->edata, CD_ORIGINDEX), mesh_final->totedge, 0); - range_vn_i( - CustomData_get_layer(&mesh_final->pdata, CD_ORIGINDEX), mesh_final->totpoly, 0); + range_vn_i((int *)CustomData_get_layer(&mesh_final->vdata, CD_ORIGINDEX), + mesh_final->totvert, + 0); + range_vn_i((int *)CustomData_get_layer(&mesh_final->edata, CD_ORIGINDEX), + mesh_final->totedge, + 0); + range_vn_i((int *)CustomData_get_layer(&mesh_final->pdata, CD_ORIGINDEX), + mesh_final->totpoly, + 0); } } @@ -1168,49 +1239,50 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* add cloth rest shape key if needed */ if (mask.vmask & CD_MASK_CLOTH_ORCO) { - add_orco_mesh(ob, NULL, mesh_final, mesh_orco, CD_CLOTH_ORCO); + add_orco_mesh(ob, nullptr, mesh_final, mesh_orco, CD_CLOTH_ORCO); } /* add an origspace layer if needed */ if ((md_datamask->mask.lmask) & CD_MASK_ORIGSPACE_MLOOP) { if (!CustomData_has_layer(&mesh_final->ldata, CD_ORIGSPACE_MLOOP)) { CustomData_add_layer( - &mesh_final->ldata, CD_ORIGSPACE_MLOOP, CD_CALLOC, NULL, mesh_final->totloop); + &mesh_final->ldata, CD_ORIGSPACE_MLOOP, CD_CALLOC, nullptr, mesh_final->totloop); mesh_init_origspace(mesh_final); } } - Mesh *mesh_next = BKE_modifier_modify_mesh(md, &mectx, mesh_final); + Mesh *mesh_next = modifier_modify_mesh_and_geometry_set( + md, mectx, ob, mesh_final, geometry_set_final); ASSERT_IS_VALID_MESH(mesh_next); if (mesh_next) { /* if the modifier returned a new mesh, release the old one */ if (mesh_final != mesh_next) { BLI_assert(mesh_final != mesh_input); - BKE_id_free(NULL, mesh_final); + BKE_id_free(nullptr, mesh_final); } mesh_final = mesh_next; if (deformed_verts) { MEM_freeN(deformed_verts); - deformed_verts = NULL; + deformed_verts = nullptr; } } /* create an orco mesh in parallel */ if (nextmask.vmask & CD_MASK_ORCO) { if (!mesh_orco) { - mesh_orco = create_orco_mesh(ob, mesh_input, NULL, CD_ORCO); + mesh_orco = create_orco_mesh(ob, mesh_input, nullptr, CD_ORCO); } nextmask.vmask &= ~CD_MASK_ORCO; - CustomData_MeshMasks temp_cddata_masks = { - .vmask = CD_MASK_ORIGINDEX, - .emask = CD_MASK_ORIGINDEX, - .fmask = CD_MASK_ORIGINDEX, - .pmask = CD_MASK_ORIGINDEX, - }; - if (mti->requiredDataMask != NULL) { + CustomData_MeshMasks temp_cddata_masks = {0}; + temp_cddata_masks.vmask = CD_MASK_ORIGINDEX; + temp_cddata_masks.emask = CD_MASK_ORIGINDEX; + temp_cddata_masks.fmask = CD_MASK_ORIGINDEX; + temp_cddata_masks.pmask = CD_MASK_ORIGINDEX; + + if (mti->requiredDataMask != nullptr) { mti->requiredDataMask(ob, md, &temp_cddata_masks); } CustomData_MeshMasks_update(&temp_cddata_masks, &nextmask); @@ -1223,7 +1295,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* if the modifier returned a new mesh, release the old one */ if (mesh_orco != mesh_next) { BLI_assert(mesh_orco != mesh_input); - BKE_id_free(NULL, mesh_orco); + BKE_id_free(nullptr, mesh_orco); } mesh_orco = mesh_next; @@ -1233,7 +1305,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* create cloth orco mesh in parallel */ if (nextmask.vmask & CD_MASK_CLOTH_ORCO) { if (!mesh_orco_cloth) { - mesh_orco_cloth = create_orco_mesh(ob, mesh_input, NULL, CD_CLOTH_ORCO); + mesh_orco_cloth = create_orco_mesh(ob, mesh_input, nullptr, CD_CLOTH_ORCO); } nextmask.vmask &= ~CD_MASK_CLOTH_ORCO; @@ -1249,7 +1321,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* if the modifier returned a new mesh, release the old one */ if (mesh_orco_cloth != mesh_next) { BLI_assert(mesh_orco != mesh_input); - BKE_id_free(NULL, mesh_orco_cloth); + BKE_id_free(nullptr, mesh_orco_cloth); } mesh_orco_cloth = mesh_next; @@ -1277,7 +1349,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, } } - BLI_linklist_free((LinkNode *)datamasks, NULL); + BLI_linklist_free((LinkNode *)datamasks, nullptr); for (md = firstmd; md; md = md->next) { BKE_modifier_free_temporary_data(md); @@ -1286,11 +1358,11 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* Yay, we are done. If we have a Mesh and deformed vertices, * we need to apply these back onto the Mesh. If we have no * Mesh then we need to build one. */ - if (mesh_final == NULL) { + if (mesh_final == nullptr) { /* Note: this check on cdmask is a bit dodgy, it handles the issue at stake here (see T68211), * but other cases might require similar handling? * Could be a good idea to define a proper CustomData_MeshMask for that then. */ - if (deformed_verts == NULL && allow_shared_mesh && + if (deformed_verts == nullptr && allow_shared_mesh && (final_datamask.lmask & CD_MASK_NORMAL) == 0 && (final_datamask.pmask & CD_MASK_NORMAL) == 0) { mesh_final = mesh_input; @@ -1302,7 +1374,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, if (deformed_verts) { BKE_mesh_vert_coords_apply(mesh_final, deformed_verts); MEM_freeN(deformed_verts); - deformed_verts = NULL; + deformed_verts = nullptr; } /* Denotes whether the object which the modifier stack came from owns the mesh or whether the @@ -1314,19 +1386,19 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* No need in ORCO layer if the mesh was not deformed or modified: undeformed mesh in this case * matches input mesh. */ if (is_own_mesh) { - add_orco_mesh(ob, NULL, mesh_final, mesh_orco, CD_ORCO); + add_orco_mesh(ob, nullptr, mesh_final, mesh_orco, CD_ORCO); } if (mesh_deform) { - add_orco_mesh(ob, NULL, mesh_deform, NULL, CD_ORCO); + add_orco_mesh(ob, nullptr, mesh_deform, nullptr, CD_ORCO); } } if (mesh_orco) { - BKE_id_free(NULL, mesh_orco); + BKE_id_free(nullptr, mesh_orco); } if (mesh_orco_cloth) { - BKE_id_free(NULL, mesh_orco_cloth); + BKE_id_free(nullptr, mesh_orco_cloth); } /* Compute normals. */ @@ -1335,16 +1407,16 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, } else { Mesh_Runtime *runtime = &mesh_input->runtime; - if (runtime->mesh_eval == NULL) { - BLI_assert(runtime->eval_mutex != NULL); - BLI_mutex_lock(runtime->eval_mutex); - if (runtime->mesh_eval == NULL) { + if (runtime->mesh_eval == nullptr) { + BLI_assert(runtime->eval_mutex != nullptr); + BLI_mutex_lock((ThreadMutex *)runtime->eval_mutex); + if (runtime->mesh_eval == nullptr) { mesh_final = BKE_mesh_copy_for_eval(mesh_input, true); mesh_calc_modifier_final_normals(mesh_input, &final_datamask, sculpt_dyntopo, mesh_final); mesh_calc_finalize(mesh_input, mesh_final); runtime->mesh_eval = mesh_final; } - BLI_mutex_unlock(runtime->eval_mutex); + BLI_mutex_unlock((ThreadMutex *)runtime->eval_mutex); } mesh_final = runtime->mesh_eval; } @@ -1358,6 +1430,9 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, if (r_deform) { *r_deform = mesh_deform; } + if (r_geometry_set) { + *r_geometry_set = new GeometrySet(std::move(geometry_set_final)); + } } float (*editbmesh_vert_coords_alloc(BMEditMesh *em, int *r_vert_len))[3] @@ -1369,7 +1444,7 @@ float (*editbmesh_vert_coords_alloc(BMEditMesh *em, int *r_vert_len))[3] *r_vert_len = em->bm->totvert; - cos = MEM_malloc_arrayN(em->bm->totvert, sizeof(float[3]), "vertexcos"); + cos = (float(*)[3])MEM_malloc_arrayN(em->bm->totvert, sizeof(float[3]), "vertexcos"); BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) { copy_v3_v3(cos[i], eve->co); @@ -1383,7 +1458,7 @@ bool editbmesh_modifier_is_enabled(Scene *scene, ModifierData *md, bool has_prev_mesh) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); const int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; if (!BKE_modifier_is_enabled(scene, md, required_mode)) { @@ -1417,10 +1492,10 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, * (BKE_mesh_calc_normals_split() assumes that if that data exists, it is always valid). */ if (do_poly_normals) { if (!CustomData_has_layer(&mesh_final->pdata, CD_NORMAL)) { - float(*polynors)[3] = CustomData_add_layer( - &mesh_final->pdata, CD_NORMAL, CD_CALLOC, NULL, mesh_final->totpoly); + float(*polynors)[3] = (float(*)[3])CustomData_add_layer( + &mesh_final->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh_final->totpoly); BKE_mesh_calc_normals_poly(mesh_final->mvert, - NULL, + nullptr, mesh_final->totvert, mesh_final->mloop, mesh_final->mpoly, @@ -1442,7 +1517,7 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, * check if the derived meshes are DM_TYPE_EDITBMESH before calling, this isn't essential * but quiets annoying error messages since tessfaces wont be created. */ if (final_datamask->fmask & CD_MASK_MFACE) { - if (mesh_final->edit_mesh == NULL) { + if (mesh_final->edit_mesh == nullptr) { BKE_mesh_tessface_ensure(mesh_final); } } @@ -1466,35 +1541,39 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, const CustomData_MeshMasks *dataMask, /* return args */ Mesh **r_cage, - Mesh **r_final) + Mesh **r_final, + GeometrySet **r_geometry_set) { /* Input and final mesh. Final mesh is only created the moment the first * constructive modifier is executed, or a deform modifier needs normals * or certain data layers. */ - Mesh *mesh_input = ob->data; - Mesh *mesh_final = NULL; - Mesh *mesh_cage = NULL; + Mesh *mesh_input = (Mesh *)ob->data; + Mesh *mesh_final = nullptr; + Mesh *mesh_cage = nullptr; + /* This geometry set contains the non-mesh data that might be generated by modifiers. */ + GeometrySet geometry_set_final; /* Deformed vertex locations array. Deform only modifier need this type of * float array rather than MVert*. Tracked along with mesh_final as an * optimization to avoid copying coordinates back and forth if there are * multiple sequential deform only modifiers. */ - float(*deformed_verts)[3] = NULL; + float(*deformed_verts)[3] = nullptr; int num_deformed_verts = 0; bool isPrevDeform = false; /* Mesh with constructive modifiers but no deformation applied. Tracked * along with final mesh if undeformed / orco coordinates are requested * for texturing. */ - Mesh *mesh_orco = NULL; + Mesh *mesh_orco = nullptr; /* Modifier evaluation modes. */ const int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); /* Modifier evaluation contexts for different types of modifiers. */ - ModifierApplyFlag apply_render = use_render ? MOD_APPLY_RENDER : 0; - const ModifierEvalContext mectx = {depsgraph, ob, MOD_APPLY_USECACHE | apply_render}; + ModifierApplyFlag apply_render = use_render ? MOD_APPLY_RENDER : (ModifierApplyFlag)0; + const ModifierEvalContext mectx = { + depsgraph, ob, (ModifierApplyFlag)(MOD_APPLY_USECACHE | apply_render)}; const ModifierEvalContext mectx_orco = {depsgraph, ob, MOD_APPLY_ORCO}; /* Get effective list of modifiers to execute. Some effects like shape keys @@ -1508,24 +1587,24 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, * subdividing them is expensive. */ CustomData_MeshMasks final_datamask = *dataMask; CDMaskLink *datamasks = BKE_modifier_calc_data_masks( - scene, ob, md, &final_datamask, required_mode, NULL, NULL); + scene, ob, md, &final_datamask, required_mode, nullptr, nullptr); CDMaskLink *md_datamask = datamasks; CustomData_MeshMasks append_mask = CD_MASK_BAREMESH; /* Evaluate modifiers up to certain index to get the mesh cage. */ - int cageIndex = BKE_modifiers_get_cage_index(scene, ob, NULL, 1); + int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, true); if (r_cage && cageIndex == -1) { mesh_cage = BKE_mesh_wrapper_from_editmesh_with_coords( - em_input, &final_datamask, NULL, mesh_input); + em_input, &final_datamask, nullptr, mesh_input); } /* Clear errors before evaluation. */ BKE_modifiers_clear_errors(ob); for (int i = 0; md; i++, md = md->next, md_datamask = md_datamask->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); - if (!editbmesh_modifier_is_enabled(scene, ob, md, mesh_final != NULL)) { + if (!editbmesh_modifier_is_enabled(scene, ob, md, mesh_final != nullptr)) { continue; } @@ -1555,11 +1634,11 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, } } else if (isPrevDeform && mti->dependsOnNormals && mti->dependsOnNormals(md)) { - if (mesh_final == NULL) { - mesh_final = BKE_mesh_from_bmesh_for_eval_nomain(em_input->bm, NULL, mesh_input); + 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); } - BLI_assert(deformed_verts != NULL); + BLI_assert(deformed_verts != nullptr); BKE_mesh_vert_coords_apply(mesh_final, deformed_verts); } @@ -1577,7 +1656,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, if (deformed_verts) { Mesh *mesh_tmp = BKE_mesh_copy_for_eval(mesh_final, false); if (mesh_final != mesh_cage) { - BKE_id_free(NULL, mesh_final); + BKE_id_free(nullptr, mesh_final); } mesh_final = mesh_tmp; BKE_mesh_vert_coords_apply(mesh_final, deformed_verts); @@ -1589,8 +1668,8 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, } else { mesh_final = BKE_mesh_wrapper_from_editmesh_with_coords( - em_input, NULL, deformed_verts, mesh_input); - deformed_verts = NULL; + em_input, nullptr, deformed_verts, mesh_input); + deformed_verts = nullptr; } /* create an orco derivedmesh in parallel */ @@ -1612,7 +1691,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, if (mesh_next) { /* if the modifier returned a new dm, release the old one */ if (mesh_orco && mesh_orco != mesh_next) { - BKE_id_free(NULL, mesh_orco); + BKE_id_free(nullptr, mesh_orco); } mesh_orco = mesh_next; } @@ -1632,23 +1711,24 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, if (mask.lmask & CD_MASK_ORIGSPACE_MLOOP) { if (!CustomData_has_layer(&mesh_final->ldata, CD_ORIGSPACE_MLOOP)) { CustomData_add_layer( - &mesh_final->ldata, CD_ORIGSPACE_MLOOP, CD_CALLOC, NULL, mesh_final->totloop); + &mesh_final->ldata, CD_ORIGSPACE_MLOOP, CD_CALLOC, nullptr, mesh_final->totloop); mesh_init_origspace(mesh_final); } } - Mesh *mesh_next = BKE_modifier_modify_mesh(md, &mectx, mesh_final); + Mesh *mesh_next = modifier_modify_mesh_and_geometry_set( + md, mectx, ob, mesh_final, geometry_set_final); ASSERT_IS_VALID_MESH(mesh_next); if (mesh_next) { if (mesh_final && mesh_final != mesh_next) { - BKE_id_free(NULL, mesh_final); + BKE_id_free(nullptr, mesh_final); } mesh_final = mesh_next; if (deformed_verts) { MEM_freeN(deformed_verts); - deformed_verts = NULL; + deformed_verts = nullptr; } } mesh_final->runtime.deformed_only = false; @@ -1668,12 +1748,12 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, if (!BKE_mesh_runtime_ensure_edit_data(me_orig)) { BKE_mesh_runtime_reset_edit_data(me_orig); } - me_orig->runtime.edit_data->vertexCos = MEM_dupallocN(deformed_verts); + me_orig->runtime.edit_data->vertexCos = (float(*)[3])MEM_dupallocN(deformed_verts); } mesh_cage = BKE_mesh_wrapper_from_editmesh_with_coords( em_input, &final_datamask, - deformed_verts ? MEM_dupallocN(deformed_verts) : NULL, + deformed_verts ? (float(*)[3])MEM_dupallocN(deformed_verts) : nullptr, mesh_input); } } @@ -1681,7 +1761,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, isPrevDeform = (mti->type == eModifierTypeType_OnlyDeform); } - BLI_linklist_free((LinkNode *)datamasks, NULL); + BLI_linklist_free((LinkNode *)datamasks, nullptr); /* Yay, we are done. If we have a DerivedMesh and deformed vertices need * to apply these back onto the DerivedMesh. If we have no DerivedMesh @@ -1690,7 +1770,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, if (deformed_verts) { Mesh *mesh_tmp = BKE_mesh_copy_for_eval(mesh_final, false); if (mesh_final != mesh_cage) { - BKE_id_free(NULL, mesh_final); + BKE_id_free(nullptr, mesh_final); } mesh_final = mesh_tmp; BKE_mesh_vert_coords_apply(mesh_final, deformed_verts); @@ -1704,7 +1784,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, /* this is just a copy of the editmesh, no need to calc normals */ mesh_final = BKE_mesh_wrapper_from_editmesh_with_coords( em_input, &final_datamask, deformed_verts, mesh_input); - deformed_verts = NULL; + deformed_verts = nullptr; } if (deformed_verts) { @@ -1720,7 +1800,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, } if (mesh_orco) { - BKE_id_free(NULL, mesh_orco); + BKE_id_free(nullptr, mesh_orco); } /* Ensure normals calculation below is correct. */ @@ -1739,6 +1819,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, if (r_cage) { *r_cage = mesh_cage; } + if (r_geometry_set) { + *r_geometry_set = new GeometrySet(std::move(geometry_set_final)); + } } static void mesh_build_extra_data(struct Depsgraph *depsgraph, Object *ob, Mesh *mesh_eval) @@ -1784,7 +1867,8 @@ static void mesh_build_data(struct Depsgraph *depsgraph, } #endif - Mesh *mesh_eval = NULL, *mesh_deform_eval = NULL; + Mesh *mesh_eval = nullptr, *mesh_deform_eval = nullptr; + GeometrySet *geometry_set_eval = nullptr; mesh_calc_modifiers(depsgraph, scene, ob, @@ -1795,7 +1879,8 @@ static void mesh_build_data(struct Depsgraph *depsgraph, true, true, &mesh_deform_eval, - &mesh_eval); + &mesh_eval, + &geometry_set_eval); /* The modifier stack evaluation is storing result in mesh->runtime.mesh_eval, but this result * is not guaranteed to be owned by object. @@ -1803,10 +1888,16 @@ static void mesh_build_data(struct Depsgraph *depsgraph, * Check ownership now, since later on we can not go to a mesh owned by someone else via * object's runtime: this could cause access freed data on depsgraph destruction (mesh who owns * the final result might be freed prior to object). */ - Mesh *mesh = ob->data; + Mesh *mesh = (Mesh *)ob->data; const bool is_mesh_eval_owned = (mesh_eval != mesh->runtime.mesh_eval); BKE_object_eval_assign_data(ob, &mesh_eval->id, is_mesh_eval_owned); + /* Add the final mesh as read-only non-owning component to the geometry set. */ + BLI_assert(!geometry_set_eval->has<MeshComponent>()); + MeshComponent &mesh_component = geometry_set_eval->get_component_for_write<MeshComponent>(); + mesh_component.replace(mesh_eval, GeometryOwnershipType::ReadOnly); + ob->runtime.geometry_set_eval = geometry_set_eval; + ob->runtime.mesh_deform_eval = mesh_deform_eval; ob->runtime.last_data_mask = *dataMask; ob->runtime.last_need_mapping = need_mapping; @@ -1816,7 +1907,7 @@ static void mesh_build_data(struct Depsgraph *depsgraph, /* Make sure that drivers can target shapekey properties. * Note that this causes a potential inconsistency, as the shapekey may have a * different topology than the evaluated mesh. */ - BLI_assert(mesh->key == NULL || DEG_is_evaluated_id(&mesh->key->id)); + BLI_assert(mesh->key == nullptr || DEG_is_evaluated_id(&mesh->key->id)); mesh_eval->key = mesh->key; if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) { @@ -1846,11 +1937,14 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph, Mesh *me_cage; Mesh *me_final; + GeometrySet *non_mesh_components; - editbmesh_calc_modifiers(depsgraph, scene, obedit, em, dataMask, &me_cage, &me_final); + editbmesh_calc_modifiers( + depsgraph, scene, obedit, em, dataMask, &me_cage, &me_final, &non_mesh_components); em->mesh_eval_final = me_final; em->mesh_eval_cage = me_cage; + obedit->runtime.geometry_set_eval = non_mesh_components; BKE_object_boundbox_calc_from_mesh(obedit, em->mesh_eval_final); @@ -1878,7 +1972,8 @@ static void object_get_datamask(const Depsgraph *depsgraph, return; } - Object *actob = view_layer->basact ? DEG_get_original_object(view_layer->basact->object) : NULL; + Object *actob = view_layer->basact ? DEG_get_original_object(view_layer->basact->object) : + nullptr; if (DEG_get_original_object(ob) == actob) { bool editing = BKE_paint_select_face_test(actob); @@ -1948,7 +2043,7 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph, object_get_datamask(depsgraph, ob, &cddata_masks, &need_mapping); Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if ((mesh_eval == NULL) || + if ((mesh_eval == nullptr) || !CustomData_MeshMasks_are_matching(&(ob->runtime.last_data_mask), &cddata_masks) || (need_mapping && !ob->runtime.last_need_mapping)) { CustomData_MeshMasks_update(&cddata_masks, &ob->runtime.last_data_mask); @@ -1957,7 +2052,7 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph, mesh_eval = BKE_object_get_evaluated_mesh(ob); } - if (mesh_eval != NULL) { + if (mesh_eval != nullptr) { BLI_assert(!(mesh_eval->runtime.cd_dirty_vert & CD_MASK_NORMAL)); } return mesh_eval; @@ -2001,7 +2096,8 @@ Mesh *mesh_create_eval_final(Depsgraph *depsgraph, { Mesh *final; - mesh_calc_modifiers(depsgraph, scene, ob, 1, false, dataMask, -1, false, false, NULL, &final); + mesh_calc_modifiers( + depsgraph, scene, ob, 1, false, dataMask, -1, false, false, nullptr, &final, nullptr); return final; } @@ -2014,7 +2110,8 @@ Mesh *mesh_create_eval_final_index_render(Depsgraph *depsgraph, { Mesh *final; - mesh_calc_modifiers(depsgraph, scene, ob, 1, false, dataMask, index, false, false, NULL, &final); + mesh_calc_modifiers( + depsgraph, scene, ob, 1, false, dataMask, index, false, false, nullptr, &final, nullptr); return final; } @@ -2026,7 +2123,8 @@ Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph, { Mesh *final; - mesh_calc_modifiers(depsgraph, scene, ob, 0, false, dataMask, -1, false, false, NULL, &final); + mesh_calc_modifiers( + depsgraph, scene, ob, 0, false, dataMask, -1, false, false, nullptr, &final, nullptr); return final; } @@ -2038,7 +2136,8 @@ Mesh *mesh_create_eval_no_deform_render(Depsgraph *depsgraph, { Mesh *final; - mesh_calc_modifiers(depsgraph, scene, ob, 0, false, dataMask, -1, false, false, NULL, &final); + mesh_calc_modifiers( + depsgraph, scene, ob, 0, false, dataMask, -1, false, false, nullptr, &final, nullptr); return final; } @@ -2058,7 +2157,7 @@ Mesh *editbmesh_get_eval_cage_and_final(Depsgraph *depsgraph, /* if there's no derived mesh or the last data mask used doesn't include * the data we need, rebuild the derived mesh */ - object_get_datamask(depsgraph, obedit, &cddata_masks, NULL); + object_get_datamask(depsgraph, obedit, &cddata_masks, nullptr); if (!em->mesh_eval_cage || !CustomData_MeshMasks_are_matching(&(em->lastDataMask), &cddata_masks)) { @@ -2083,7 +2182,7 @@ Mesh *editbmesh_get_eval_cage(struct Depsgraph *depsgraph, /* if there's no derived mesh or the last data mask used doesn't include * the data we need, rebuild the derived mesh */ - object_get_datamask(depsgraph, obedit, &cddata_masks, NULL); + object_get_datamask(depsgraph, obedit, &cddata_masks, nullptr); if (!em->mesh_eval_cage || !CustomData_MeshMasks_are_matching(&(em->lastDataMask), &cddata_masks)) { @@ -2108,10 +2207,10 @@ Mesh *editbmesh_get_eval_cage_from_orig(struct Depsgraph *depsgraph, /***/ /* same as above but for vert coords */ -typedef struct { +struct MappedUserData { float (*vertexcos)[3]; BLI_bitmap *vertex_visit; -} MappedUserData; +}; static void make_vertexcos__mapFunc(void *userData, int index, @@ -2153,30 +2252,32 @@ void DM_calc_loop_tangents(DerivedMesh *dm, const char (*tangent_names)[MAX_NAME], int tangent_names_len) { - BKE_mesh_calc_loop_tangent_ex(dm->getVertArray(dm), - dm->getPolyArray(dm), - dm->getNumPolys(dm), - dm->getLoopArray(dm), - dm->getLoopTriArray(dm), - dm->getNumLoopTri(dm), - &dm->loopData, - calc_active_tangent, - tangent_names, - tangent_names_len, - CustomData_get_layer(&dm->polyData, CD_NORMAL), - dm->getLoopDataArray(dm, CD_NORMAL), - dm->getVertDataArray(dm, CD_ORCO), /* may be NULL */ - /* result */ - &dm->loopData, - dm->getNumLoops(dm), - &dm->tangent_mask); + BKE_mesh_calc_loop_tangent_ex( + dm->getVertArray(dm), + dm->getPolyArray(dm), + dm->getNumPolys(dm), + dm->getLoopArray(dm), + dm->getLoopTriArray(dm), + dm->getNumLoopTri(dm), + &dm->loopData, + calc_active_tangent, + tangent_names, + tangent_names_len, + (float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL), + (float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), + (float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ + /* result */ + &dm->loopData, + dm->getNumLoops(dm), + &dm->tangent_mask); } static void mesh_init_origspace(Mesh *mesh) { const float default_osf[4][2] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; - OrigSpaceLoop *lof_array = CustomData_get_layer(&mesh->ldata, CD_ORIGSPACE_MLOOP); + OrigSpaceLoop *lof_array = (OrigSpaceLoop *)CustomData_get_layer(&mesh->ldata, + CD_ORIGSPACE_MLOOP); const int numpoly = mesh->totpoly; // const int numloop = mesh->totloop; MVert *mv = mesh->mvert; @@ -2184,8 +2285,7 @@ static void mesh_init_origspace(Mesh *mesh) MPoly *mp = mesh->mpoly; int i, j, k; - float(*vcos_2d)[2] = NULL; - BLI_array_staticdeclare(vcos_2d, 64); + blender::Vector<blender::float2, 64> vcos_2d; for (i = 0; i < numpoly; i++, mp++) { OrigSpaceLoop *lof = lof_array + mp->loopstart; @@ -2206,8 +2306,7 @@ static void mesh_init_origspace(Mesh *mesh) BKE_mesh_calc_poly_normal(mp, l, mv, p_nor); axis_dominant_v3_to_m3(mat, p_nor); - BLI_array_clear(vcos_2d); - BLI_array_reserve(vcos_2d, mp->totloop); + vcos_2d.resize(mp->totloop); for (j = 0; j < mp->totloop; j++, l++) { mul_v3_m3v3(co, mat, mv[l->v].co); copy_v2_v2(vcos_2d[j], co); @@ -2245,7 +2344,6 @@ static void mesh_init_origspace(Mesh *mesh) } BKE_mesh_tessface_clear(mesh); - BLI_array_free(vcos_2d); } /* derivedmesh info printing function, @@ -2367,7 +2465,7 @@ bool DM_is_valid(DerivedMesh *dm) do_fixes, &changed); - is_valid &= BKE_mesh_validate_arrays(NULL, + is_valid &= BKE_mesh_validate_arrays(nullptr, dm->getVertArray(dm), dm->getNumVerts(dm), dm->getEdgeArray(dm), @@ -2378,7 +2476,7 @@ bool DM_is_valid(DerivedMesh *dm) dm->getNumLoops(dm), dm->getPolyArray(dm), dm->getNumPolys(dm), - dm->getVertDataArray(dm, CD_MDEFORMVERT), + (MDeformVert *)dm->getVertDataArray(dm, CD_MDEFORMVERT), do_verbose, do_fixes, &changed); diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 13abfa92032..633d6202222 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -42,6 +42,7 @@ #include "DNA_ID.h" #include "DNA_anim_types.h" #include "DNA_light_types.h" +#include "DNA_material_types.h" #include "DNA_node_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 03c812b3b3d..ea41495d097 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1189,7 +1189,7 @@ static void nlaeval_free(NlaEvalData *nlaeval) /* ---------------------- */ -static int nlaevalchan_validate_index(NlaEvalChannel *nec, int index) +static int nlaevalchan_validate_index(const NlaEvalChannel *nec, int index) { if (nec->is_array) { if (index >= 0 && index < nec->base_snapshot.length) { @@ -1201,6 +1201,28 @@ static int nlaevalchan_validate_index(NlaEvalChannel *nec, int index) return 0; } +static bool nlaevalchan_validate_index_ex(const NlaEvalChannel *nec, const int array_index) +{ + /** Although array_index comes from fcurve, that doesn't necessarily mean the property has that + * many elements. */ + const int index = nlaevalchan_validate_index(nec, array_index); + + if (index < 0) { + if (G.debug & G_DEBUG) { + ID *id = nec->key.ptr.owner_id; + CLOG_WARN(&LOG, + "Animation: Invalid array index. ID = '%s', '%s[%d]', array length is %d", + id ? (id->name + 2) : "<No ID>", + nec->rna_path, + array_index, + nec->base_snapshot.length); + } + + return false; + } + return true; +} + /* Initialize default values for NlaEvalChannel from the property data. */ static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values) { @@ -1455,7 +1477,7 @@ static float nla_combine_value( return old_value + (value - base_value) * inf; case NEC_MIX_MULTIPLY: - if (base_value == 0.0f) { + if (IS_EQF(base_value, 0.0f)) { base_value = 1.0f; } return old_value * powf(value / base_value, inf); @@ -1471,6 +1493,11 @@ static float nla_combine_value( static bool nla_invert_blend_value( int blend_mode, float old_value, float target_value, float influence, float *r_value) { + /** No solution if strip had 0 influence. */ + if (IS_EQF(influence, 0.0f)) { + return false; + } + switch (blend_mode) { case NLASTRIP_MODE_ADD: *r_value = (target_value - old_value) / influence; @@ -1481,9 +1508,9 @@ static bool nla_invert_blend_value( return true; case NLASTRIP_MODE_MULTIPLY: - if (old_value == 0.0f) { + if (IS_EQF(old_value, 0.0f)) { /* Resolve 0/0 to 1. */ - if (target_value == 0.0f) { + if (IS_EQF(target_value, 0.0f)) { *r_value = 1.0f; return true; } @@ -1514,6 +1541,11 @@ static bool nla_invert_combine_value(int mix_mode, float influence, float *r_value) { + /* No solution if strip had no influence. */ + if (IS_EQF(influence, 0.0f)) { + return false; + } + switch (mix_mode) { case NEC_MIX_ADD: case NEC_MIX_AXIS_ANGLE: @@ -1521,12 +1553,12 @@ static bool nla_invert_combine_value(int mix_mode, return true; case NEC_MIX_MULTIPLY: - if (base_value == 0.0f) { + if (IS_EQF(base_value, 0.0f)) { base_value = 1.0f; } - if (old_value == 0.0f) { + if (IS_EQF(old_value, 0.0f)) { /* Resolve 0/0 to 1. */ - if (target_value == 0.0f) { + if (IS_EQF(target_value, 0.0f)) { *r_value = base_value; return true; } @@ -1560,11 +1592,14 @@ static void nla_combine_quaternion(const float old_values[4], } /* invert accumulation of quaternion channels for Combine mode according to influence */ -static void nla_invert_combine_quaternion(const float old_values[4], +static bool nla_invert_combine_quaternion(const float old_values[4], const float values[4], float influence, float result[4]) { + if (IS_EQF(influence, 0.0f)) { + return false; + } float tmp_old[4], tmp_new[4]; normalize_qt_qt(tmp_old, old_values); @@ -1573,6 +1608,8 @@ static void nla_invert_combine_quaternion(const float old_values[4], mul_qt_qtqt(result, tmp_old, tmp_new); pow_qt_fl_normalized(result, 1.0f / influence); + + return true; } /* Data about the current blend mode. */ @@ -1612,19 +1649,7 @@ static bool nlaeval_blend_value(NlaBlendData *blend, return false; } - int index = nlaevalchan_validate_index(nec, array_index); - - if (index < 0) { - if (G.debug & G_DEBUG) { - ID *id = nec->key.ptr.owner_id; - CLOG_WARN(&LOG, - "Animato: Invalid array index. ID = '%s', '%s[%d]', array length is %d", - id ? (id->name + 2) : "<No ID>", - nec->rna_path, - array_index, - nec->base_snapshot.length); - } - + if (!nlaevalchan_validate_index_ex(nec, array_index)) { return false; } @@ -1633,21 +1658,21 @@ static bool nlaeval_blend_value(NlaBlendData *blend, BLI_bitmap_set_all(nec->valid.ptr, true, 4); } else { - BLI_BITMAP_ENABLE(nec->valid.ptr, index); + BLI_BITMAP_ENABLE(nec->valid.ptr, array_index); } NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_ensure_channel(blend->snapshot, nec); - float *p_value = &nec_snapshot->values[index]; + float *p_value = &nec_snapshot->values[array_index]; if (blend->mode == NLASTRIP_MODE_COMBINE) { /* Quaternion blending is deferred until all sub-channel values are known. */ if (nec->mix_mode == NEC_MIX_QUATERNION) { NlaEvalChannelSnapshot *blend_snapshot = nlaevalchan_queue_blend(blend, nec); - blend_snapshot->values[index] = value; + blend_snapshot->values[array_index] = value; } else { - float base_value = nec->base_snapshot.values[index]; + float base_value = nec->base_snapshot.values[array_index]; *p_value = nla_combine_value(nec->mix_mode, base_value, *p_value, value, blend->influence); } @@ -2502,7 +2527,9 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, *r_force_all = true; - nla_invert_combine_quaternion(old_values, values, influence, values); + if (!nla_invert_combine_quaternion(old_values, values, influence, values)) { + return false; + } } else { float *base_values = nec->base_snapshot.values; diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index b1462167edd..ae0c27635a6 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -36,6 +36,8 @@ #include "BKE_appdir.h" /* own include */ #include "BKE_blender_version.h" +#include "BLT_translation.h" + #include "GHOST_Path-api.h" #include "MEM_guardedalloc.h" @@ -146,47 +148,74 @@ static char *blender_version_decimal(const int version) * \{ */ /** - * This is now only used to really get the user's default document folder. + * Get the folder that's the "natural" starting point for browsing files on an OS. On Unix that is + * $HOME, on Windows it is %userprofile%/Documents. * - * \note On Windows `Users/{MyUserName}/Documents` is used - * as it's the default location to save documents. + * \note On Windows `Users/{MyUserName}/Documents` is used as it's the default location to save + * documents. */ const char *BKE_appdir_folder_default(void) { #ifndef WIN32 - const char *const xdg_documents_dir = BLI_getenv("XDG_DOCUMENTS_DIR"); + return BLI_getenv("HOME"); +#else /* Windows */ + static char documentfolder[MAXPATHLEN]; - if (xdg_documents_dir) { - return xdg_documents_dir; + if (BKE_appdir_folder_documents(documentfolder)) { + return documentfolder; } + return NULL; +#endif /* WIN32 */ +} + +/** + * Get the user's home directory, i.e. $HOME on UNIX, %userprofile% on Windows. + */ +const char *BKE_appdir_folder_home(void) +{ +#ifndef WIN32 return BLI_getenv("HOME"); -#else /* Windows */ - static char documentfolder[MAXPATHLEN]; - HRESULT hResult; +#else /* Windows */ + return BLI_getenv("userprofile"); +#endif +} - /* Check for `%HOME%` environment variable. */ - if (uput_getenv("HOME", documentfolder, MAXPATHLEN)) { - if (BLI_is_dir(documentfolder)) { - return documentfolder; - } +/** + * Get the user's document directory, i.e. $HOME/Documents on Linux, %userprofile%/Documents on + * Windows. If this can't be found using OS queries (via Ghost), try manually finding it. + * + * \returns True if the path is valid and points to an existing directory. + */ +bool BKE_appdir_folder_documents(char *dir) +{ + dir[0] = '\0'; + + const char *documents_path = (const char *)GHOST_getUserSpecialDir( + GHOST_kUserSpecialDirDocuments); + + /* Usual case: Ghost gave us the documents path. We're done here. */ + if (documents_path && BLI_is_dir(documents_path)) { + BLI_strncpy(dir, documents_path, FILE_MAXDIR); + return true; } - /* Add user profile support for WIN 2K / NT. - * This is `%APPDATA%`, which translates to either: - * - `%USERPROFILE%\Application Data` or... - * - `%USERPROFILE%\AppData\Roaming` (since Vista). - */ - hResult = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, documentfolder); + /* Ghost couldn't give us a documents path, let's try if we can find it ourselves.*/ - if (hResult == S_OK) { - if (BLI_is_dir(documentfolder)) { - return documentfolder; - } + const char *home_path = BKE_appdir_folder_home(); + if (!home_path || !BLI_is_dir(home_path)) { + return false; } - return NULL; -#endif /* WIN32 */ + char try_documents_path[FILE_MAXDIR]; + /* Own attempt at getting a valid Documents path. */ + BLI_path_join(try_documents_path, sizeof(try_documents_path), home_path, N_("Documents"), NULL); + if (!BLI_is_dir(try_documents_path)) { + return false; + } + + BLI_strncpy(dir, try_documents_path, FILE_MAXDIR); + return true; } /** @@ -877,14 +906,20 @@ bool BKE_appdir_program_python_search(char *fullpath, const char *python_build_def = STRINGIFY(PYTHON_EXECUTABLE_NAME); #endif const char *basename = "python"; +#if defined(WIN32) && !defined(NDEBUG) + const char *basename_debug = "python_d"; +#endif char python_version[16]; /* Check both possible names. */ const char *python_names[] = { #ifdef PYTHON_EXECUTABLE_NAME - python_build_def, + python_build_def, +#endif +#if defined(WIN32) && !defined(NDEBUG) + basename_debug, #endif - python_version, - basename, + python_version, + basename, }; bool is_found = false; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 92146082557..ced211b1926 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2441,7 +2441,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected) } /** - * \param r_last_visited_bone_p the last bone handled by the last call to this function. + * \param r_last_visited_bone_p: The last bone handled by the last call to this function. */ static int rebuild_pose_bone( bPose *pose, Bone *bone, bPoseChannel *parchan, int counter, Bone **r_last_visited_bone_p) diff --git a/source/blender/blenkernel/intern/asset.c b/source/blender/blenkernel/intern/asset.c new file mode 100644 index 00000000000..7ccb0aa2b57 --- /dev/null +++ b/source/blender/blenkernel/intern/asset.c @@ -0,0 +1,151 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + */ + +#include <string.h> + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" + +#include "BKE_asset.h" +#include "BKE_icons.h" +#include "BKE_idprop.h" + +#include "DNA_ID.h" +#include "DNA_asset_types.h" +#include "DNA_defaults.h" + +#include "BLO_read_write.h" + +#include "MEM_guardedalloc.h" + +AssetMetaData *BKE_asset_metadata_create(void) +{ + AssetMetaData *asset_data = MEM_callocN(sizeof(*asset_data), __func__); + memcpy(asset_data, DNA_struct_default_get(AssetMetaData), sizeof(*asset_data)); + return asset_data; +} + +void BKE_asset_metadata_free(AssetMetaData **asset_data) +{ + if ((*asset_data)->properties) { + IDP_FreeProperty((*asset_data)->properties); + } + MEM_SAFE_FREE((*asset_data)->description); + BLI_freelistN(&(*asset_data)->tags); + + MEM_SAFE_FREE(*asset_data); +} + +static AssetTag *asset_metadata_tag_add(AssetMetaData *asset_data, const char *const name) +{ + AssetTag *tag = MEM_callocN(sizeof(*tag), __func__); + BLI_strncpy(tag->name, name, sizeof(tag->name)); + + BLI_addtail(&asset_data->tags, tag); + asset_data->tot_tags++; + /* Invariant! */ + BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags); + + return tag; +} + +AssetTag *BKE_asset_metadata_tag_add(AssetMetaData *asset_data, const char *name) +{ + AssetTag *tag = asset_metadata_tag_add(asset_data, name); + BLI_uniquename(&asset_data->tags, tag, name, '.', offsetof(AssetTag, name), sizeof(tag->name)); + return tag; +} + +/** + * Make sure there is a tag with name \a name, create one if needed. + */ +struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(AssetMetaData *asset_data, + const char *name) +{ + struct AssetTagEnsureResult result = {.tag = NULL}; + if (!name[0]) { + return result; + } + + AssetTag *tag = BLI_findstring(&asset_data->tags, name, offsetof(AssetTag, name)); + + if (tag) { + result.tag = tag; + result.is_new = false; + return result; + } + + tag = asset_metadata_tag_add(asset_data, name); + + result.tag = tag; + result.is_new = true; + return result; +} + +void BKE_asset_metadata_tag_remove(AssetMetaData *asset_data, AssetTag *tag) +{ + BLI_assert(BLI_findindex(&asset_data->tags, tag) >= 0); + BLI_freelinkN(&asset_data->tags, tag); + asset_data->tot_tags--; + /* Invariant! */ + BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags); +} + +/* Queries -------------------------------------------- */ + +PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data), + const ID *id) +{ + return BKE_previewimg_id_get(id); +} + +/* .blend file API -------------------------------------------- */ + +void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data) +{ + BLO_write_struct(writer, AssetMetaData, asset_data); + + if (asset_data->properties) { + IDP_BlendWrite(writer, asset_data->properties); + } + + if (asset_data->description) { + BLO_write_string(writer, asset_data->description); + } + LISTBASE_FOREACH (AssetTag *, tag, &asset_data->tags) { + BLO_write_struct(writer, AssetTag, tag); + } +} + +void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data) +{ + /* asset_data itself has been read already. */ + + if (asset_data->properties) { + BLO_read_data_address(reader, &asset_data->properties); + IDP_BlendDataRead(reader, &asset_data->properties); + } + + BLO_read_data_address(reader, &asset_data->description); + BLO_read_list(reader, &asset_data->tags); + BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags); +} diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d79168d5443..934beb8a848 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -392,6 +392,8 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty return &CPPType::get<int>(); case CD_PROP_COLOR: return &CPPType::get<Color4f>(); + case CD_PROP_BOOL: + return &CPPType::get<bool>(); default: return nullptr; } @@ -415,6 +417,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) if (type.is<Color4f>()) { return CD_PROP_COLOR; } + if (type.is<bool>()) { + return CD_PROP_BOOL; + } return static_cast<CustomDataType>(-1); } @@ -449,6 +454,9 @@ static ReadAttributePtr read_attribute_from_custom_data(const CustomData &custom case CD_PROP_COLOR: return std::make_unique<ArrayReadAttribute<Color4f>>( domain, Span(static_cast<Color4f *>(layer.data), size)); + case CD_PROP_BOOL: + return std::make_unique<ArrayReadAttribute<bool>>( + domain, Span(static_cast<bool *>(layer.data), size)); } } } @@ -490,6 +498,9 @@ static WriteAttributePtr write_attribute_from_custom_data( case CD_PROP_COLOR: return std::make_unique<ArrayWriteAttribute<Color4f>>( domain, MutableSpan(static_cast<Color4f *>(layer.data), size)); + case CD_PROP_BOOL: + return std::make_unique<ArrayWriteAttribute<bool>>( + domain, MutableSpan(static_cast<bool *>(layer.data), size)); } } } @@ -590,6 +601,15 @@ Set<std::string> GeometryComponent::attribute_names() const return {}; } +bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const +{ + ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); + if (attribute) { + return true; + } + return false; +} + static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute, const blender::fn::CPPType &to_type) { @@ -640,6 +660,28 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read( return attribute; } +ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name, + const AttributeDomain domain) const +{ + if (!this->attribute_domain_supported(domain)) { + return {}; + } + + ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); + if (!attribute) { + return {}; + } + + if (attribute->domain() != domain) { + attribute = this->attribute_try_adapt_domain(std::move(attribute), domain); + if (!attribute) { + return {}; + } + } + + return attribute; +} + ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name, const AttributeDomain domain, const CustomDataType data_type, @@ -742,6 +784,7 @@ bool PointCloudComponent::attribute_domain_with_type_supported( const AttributeDomain domain, const CustomDataType data_type) const { return domain == ATTR_DOMAIN_POINT && ELEM(data_type, + CD_PROP_BOOL, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, @@ -865,8 +908,13 @@ bool MeshComponent::attribute_domain_with_type_supported(const AttributeDomain d if (!this->attribute_domain_supported(domain)) { return false; } - return ELEM( - data_type, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR); + return ELEM(data_type, + CD_PROP_BOOL, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_INT32, + CD_PROP_COLOR); } int MeshComponent::attribute_domain_size(const AttributeDomain domain) const diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index f4f25c3a153..5b5bd416ef2 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -296,6 +296,7 @@ void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts) } BLI_freelistN(&userdef->autoexec_paths); + BLI_freelistN(&userdef->asset_libraries); BLI_freelistN(&userdef->uistyles); BLI_freelistN(&userdef->uifonts); diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 567773507cf..0855db1a943 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -52,6 +52,7 @@ #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_preferences.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -645,6 +646,8 @@ UserDef *BKE_blendfile_userdef_from_defaults(void) /* Default studio light. */ BKE_studiolight_default(userdef->light_param, userdef->light_ambient); + BKE_preferences_asset_library_default_add(userdef); + return userdef; } diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c index 96bf9fbe8d2..e69173cc1d5 100644 --- a/source/blender/blenkernel/intern/boids.c +++ b/source/blender/blenkernel/intern/boids.c @@ -39,6 +39,7 @@ #include "BKE_collision.h" #include "BKE_effect.h" #include "BKE_particle.h" +#include "BLI_kdopbvh.h" #include "BKE_modifier.h" diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 96791aed2c3..9a954a89cad 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -23,6 +23,7 @@ #include "DNA_brush_types.h" #include "DNA_defaults.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -1833,6 +1834,14 @@ void BKE_brush_sculpt_reset(Brush *br) br->flag &= ~BRUSH_SPACE_ATTEN; br->curve_preset = BRUSH_CURVE_SPHERE; break; + case SCULPT_TOOL_DISPLACEMENT_SMEAR: + br->alpha = 1.0f; + br->spacing = 5; + br->hardness = 0.7f; + br->flag &= ~BRUSH_ALPHA_PRESSURE; + br->flag &= ~BRUSH_SPACE_ATTEN; + br->curve_preset = BRUSH_CURVE_SMOOTHER; + break; default: break; } @@ -1897,6 +1906,7 @@ void BKE_brush_sculpt_reset(Brush *br) case SCULPT_TOOL_MASK: case SCULPT_TOOL_DRAW_FACE_SETS: case SCULPT_TOOL_DISPLACEMENT_ERASER: + case SCULPT_TOOL_DISPLACEMENT_SMEAR: br->add_col[0] = 0.75f; br->add_col[1] = 0.75f; br->add_col[2] = 0.75f; diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 5eec788255d..b86b59066d6 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1773,6 +1773,15 @@ static void layer_collection_flags_store(Main *bmain, } } +static void layer_collection_flags_free_recursive(LayerCollectionFlag *flag) +{ + LISTBASE_FOREACH (LayerCollectionFlag *, child, &flag->children) { + layer_collection_flags_free_recursive(child); + } + + BLI_freelistN(&flag->children); +} + static void layer_collection_flags_restore_recursive(LayerCollection *layer_collection, LayerCollectionFlag *flag) { @@ -1788,7 +1797,6 @@ static void layer_collection_flags_restore_recursive(LayerCollection *layer_coll child_flag = child_flag->next; } - BLI_freelistN(&flag->children); /* We treat exclude as a special case. * @@ -1814,10 +1822,15 @@ static void layer_collection_flags_restore(ListBase *flags, const Collection *co LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection( view_layer, collection); - /* The flags should only be added if the collection is in the view layer. */ - BLI_assert(layer_collection != NULL); - - layer_collection_flags_restore_recursive(layer_collection, flag); + /* Check that the collection is still in the scene (and therefore its view layers). In most + * cases this is true, but if we move a sub-collection shared by several scenes to a collection + * local to the target scene, it is effectively removed from every other scene's hierarchy + * (e.g. moving into current scene's master collection). Then the other scene's view layers + * won't contain a matching layer collection anymore, so there is nothing to restore to. */ + if (layer_collection != NULL) { + layer_collection_flags_restore_recursive(layer_collection, flag); + } + layer_collection_flags_free_recursive(flag); } BLI_freelistN(flags); @@ -1877,7 +1890,7 @@ bool BKE_collection_move(Main *bmain, /* Create and remove layer collections. */ BKE_main_collection_sync(bmain); - /* Restore the original layer collection flags. */ + /* Restore the original layer collection flags and free their temporary storage. */ if (do_flag_sync) { layer_collection_flags_restore(&layer_flags, collection); } diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 1c17692ac36..b6f84dfc42f 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -5960,7 +5960,7 @@ static bConstraint *constraint_find_original_for_update(bConstraintOb *cob, bCon * Check whether given constraint is not local (i.e. from linked data) when the object is a library * override. * - * \param con May be NULL, in which case we consider it as a non-local constraint case. + * \param con: May be NULL, in which case we consider it as a non-local constraint case. */ bool BKE_constraint_is_nonlocal_in_liboverride(const Object *ob, const bConstraint *con) { diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index a1edfd1c56d..65accc66084 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -123,7 +123,7 @@ void CTX_free(bContext *C) /* store */ -bContextStore *CTX_store_add(ListBase *contexts, const char *name, PointerRNA *ptr) +bContextStore *CTX_store_add(ListBase *contexts, const char *name, const PointerRNA *ptr) { /* ensure we have a context to put the entry in, if it was already used * we have to copy the context to ensure */ @@ -178,6 +178,11 @@ bContextStore *CTX_store_add_all(ListBase *contexts, bContextStore *context) return ctx; } +bContextStore *CTX_store_get(bContext *C) +{ + return C->wm.store; +} + void CTX_store_set(bContext *C, bContextStore *store) { C->wm.store = store; @@ -1202,6 +1207,11 @@ ToolSettings *CTX_data_tool_settings(const bContext *C) return NULL; } +int CTX_data_selected_ids(const bContext *C, ListBase *list) +{ + return ctx_data_collection_get(C, "selected_ids", list); +} + int CTX_data_selected_nodes(const bContext *C, ListBase *list) { return ctx_data_collection_get(C, "selected_nodes", list); diff --git a/source/blender/blenkernel/intern/cryptomatte.c b/source/blender/blenkernel/intern/cryptomatte.c deleted file mode 100644 index 6570ffce920..00000000000 --- a/source/blender/blenkernel/intern/cryptomatte.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup bke - */ - -#include "BKE_cryptomatte.h" - -#include "DNA_material_types.h" -#include "DNA_object_types.h" - -#include "BLI_compiler_attrs.h" -#include "BLI_hash_mm3.h" -#include "BLI_string.h" -#include <string.h> - -static uint32_t cryptomatte_hash(const ID *id) -{ - const char *name = &id->name[2]; - const int len = BLI_strnlen(name, MAX_NAME - 2); - uint32_t cryptohash_int = BLI_hash_mm3((const unsigned char *)name, len, 0); - return cryptohash_int; -} - -uint32_t BKE_cryptomatte_object_hash(const Object *object) -{ - return cryptomatte_hash(&object->id); -} - -uint32_t BKE_cryptomatte_material_hash(const Material *material) -{ - if (material == NULL) { - return 0.0f; - } - return cryptomatte_hash(&material->id); -} - -uint32_t BKE_cryptomatte_asset_hash(const Object *object) -{ - const Object *asset_object = object; - while (asset_object->parent != NULL) { - asset_object = asset_object->parent; - } - return cryptomatte_hash(&asset_object->id); -} - -/* Convert a cryptomatte hash to a float. - * - * Cryptomatte hashes are stored in float textures and images. The conversion is taken from the - * cryptomatte specification. See Floating point conversion section in - * https://github.com/Psyop/Cryptomatte/blob/master/specification/cryptomatte_specification.pdf. - * - * The conversion uses as many 32 bit floating point values as possible to minimize hash - * collisions. Unfortunately not all 32 bits can be as NaN and Inf can be problematic. - * - * Note that this conversion assumes to be running on a L-endian system. */ -float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash) -{ - uint32_t mantissa = cryptomatte_hash & ((1 << 23) - 1); - uint32_t exponent = (cryptomatte_hash >> 23) & ((1 << 8) - 1); - exponent = MAX2(exponent, (uint32_t)1); - exponent = MIN2(exponent, (uint32_t)254); - exponent = exponent << 23; - uint32_t sign = (cryptomatte_hash >> 31); - sign = sign << 31; - uint32_t float_bits = sign | exponent | mantissa; - float f; - memcpy(&f, &float_bits, sizeof(uint32_t)); - return f; -} diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc new file mode 100644 index 00000000000..4bbeb088628 --- /dev/null +++ b/source/blender/blenkernel/intern/cryptomatte.cc @@ -0,0 +1,190 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#include "BKE_cryptomatte.h" +#include "BKE_main.h" + +#include "DNA_material_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" + +#include "BLI_compiler_attrs.h" +#include "BLI_dynstr.h" +#include "BLI_hash_mm3.h" +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "MEM_guardedalloc.h" + +#include <cstring> +#include <sstream> +#include <string> + +static uint32_t cryptomatte_hash(const ID *id) +{ + const char *name = &id->name[2]; + const int name_len = BLI_strnlen(name, MAX_NAME); + uint32_t cryptohash_int = BKE_cryptomatte_hash(name, name_len); + return cryptohash_int; +} + +uint32_t BKE_cryptomatte_hash(const char *name, int name_len) +{ + uint32_t cryptohash_int = BLI_hash_mm3((const unsigned char *)name, name_len, 0); + return cryptohash_int; +} + +uint32_t BKE_cryptomatte_object_hash(const Object *object) +{ + return cryptomatte_hash(&object->id); +} + +uint32_t BKE_cryptomatte_material_hash(const Material *material) +{ + if (material == nullptr) { + return 0.0f; + } + return cryptomatte_hash(&material->id); +} + +uint32_t BKE_cryptomatte_asset_hash(const Object *object) +{ + const Object *asset_object = object; + while (asset_object->parent != nullptr) { + asset_object = asset_object->parent; + } + return cryptomatte_hash(&asset_object->id); +} + +/* Convert a cryptomatte hash to a float. + * + * Cryptomatte hashes are stored in float textures and images. The conversion is taken from the + * cryptomatte specification. See Floating point conversion section in + * https://github.com/Psyop/Cryptomatte/blob/master/specification/cryptomatte_specification.pdf. + * + * The conversion uses as many 32 bit floating point values as possible to minimize hash + * collisions. Unfortunately not all 32 bits can be as NaN and Inf can be problematic. + * + * Note that this conversion assumes to be running on a L-endian system. */ +float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash) +{ + uint32_t mantissa = cryptomatte_hash & ((1 << 23) - 1); + uint32_t exponent = (cryptomatte_hash >> 23) & ((1 << 8) - 1); + exponent = MAX2(exponent, (uint32_t)1); + exponent = MIN2(exponent, (uint32_t)254); + exponent = exponent << 23; + uint32_t sign = (cryptomatte_hash >> 31); + sign = sign << 31; + uint32_t float_bits = sign | exponent | mantissa; + float f; + memcpy(&f, &float_bits, sizeof(uint32_t)); + return f; +} + +static ID *cryptomatte_find_id(const ListBase *ids, const float encoded_hash) +{ + LISTBASE_FOREACH (ID *, id, ids) { + uint32_t hash = BKE_cryptomatte_hash((id->name + 2), BLI_strnlen(id->name + 2, MAX_NAME)); + if (BKE_cryptomatte_hash_to_float(hash) == encoded_hash) { + return id; + } + } + return nullptr; +} + +/* Find an ID in the given main that matches the given encoded float. */ +static struct ID *BKE_cryptomatte_find_id(const Main *bmain, const float encoded_hash) +{ + ID *result; + result = cryptomatte_find_id(&bmain->objects, encoded_hash); + if (result == nullptr) { + result = cryptomatte_find_id(&bmain->materials, encoded_hash); + } + return result; +} + +char *BKE_cryptomatte_entries_to_matte_id(NodeCryptomatte *node_storage) +{ + DynStr *matte_id = BLI_dynstr_new(); + bool first = true; + LISTBASE_FOREACH (CryptomatteEntry *, entry, &node_storage->entries) { + if (!first) { + BLI_dynstr_append(matte_id, ","); + } + if (BLI_strnlen(entry->name, sizeof(entry->name)) != 0) { + BLI_dynstr_nappend(matte_id, entry->name, sizeof(entry->name)); + } + else { + BLI_dynstr_appendf(matte_id, "<%.9g>", entry->encoded_hash); + } + first = false; + } + char *result = BLI_dynstr_get_cstring(matte_id); + BLI_dynstr_free(matte_id); + return result; +} + +void BKE_cryptomatte_matte_id_to_entries(const Main *bmain, + NodeCryptomatte *node_storage, + const char *matte_id) +{ + BLI_freelistN(&node_storage->entries); + + std::istringstream ss(matte_id); + while (ss.good()) { + CryptomatteEntry *entry = nullptr; + std::string token; + getline(ss, token, ','); + /* Ignore empty tokens. */ + if (token.length() > 0) { + size_t first = token.find_first_not_of(' '); + size_t last = token.find_last_not_of(' '); + if (first == std::string::npos || last == std::string::npos) { + break; + } + token = token.substr(first, (last - first + 1)); + if (*token.begin() == '<' && *(--token.end()) == '>') { + float encoded_hash = atof(token.substr(1, token.length() - 2).c_str()); + entry = (CryptomatteEntry *)MEM_callocN(sizeof(CryptomatteEntry), __func__); + entry->encoded_hash = encoded_hash; + if (bmain) { + ID *id = BKE_cryptomatte_find_id(bmain, encoded_hash); + if (id != nullptr) { + BLI_strncpy(entry->name, id->name + 2, sizeof(entry->name)); + } + } + } + else { + const char *name = token.c_str(); + int name_len = token.length(); + entry = (CryptomatteEntry *)MEM_callocN(sizeof(CryptomatteEntry), __func__); + BLI_strncpy(entry->name, name, sizeof(entry->name)); + uint32_t hash = BKE_cryptomatte_hash(name, name_len); + entry->encoded_hash = BKE_cryptomatte_hash_to_float(hash); + } + } + if (entry != nullptr) { + BLI_addtail(&node_storage->entries, entry); + } + } +}
\ No newline at end of file diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index fdb3e246382..1e2bc570c65 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1837,6 +1837,21 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { layerMultiply_propfloat2, NULL, layerAdd_propfloat2}, + /* 50: CD_PROP_POOL */ + {sizeof(bool), + "bool", + 1, + N_("Boolean"), + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL}, }; static const char *LAYERTYPENAMES[CD_NUMTYPES] = { @@ -1892,6 +1907,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { "CDPropCol", "CDPropFloat3", "CDPropFloat2", + "CDPropBoolean", }; const CustomData_MeshMasks CD_MASK_BAREMESH = { diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index b56a15b3d45..e18b2d87459 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -42,6 +42,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" +#include "DNA_object_force_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_texture_types.h" diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 9431915b4e4..df1dbaa905f 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -125,11 +125,17 @@ static void vfont_free_data(ID *id) static void vfont_blend_write(BlendWriter *writer, ID *id, const void *id_address) { VFont *vf = (VFont *)id; - if (vf->id.us > 0 || BLO_write_is_undo(writer)) { + const bool is_undo = BLO_write_is_undo(writer); + if (vf->id.us > 0 || is_undo) { /* Clean up, important in undo case to reduce false detection of changed datablocks. */ vf->data = NULL; vf->temp_pf = NULL; + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(vf) && !is_undo) { + vf->packedfile = NULL; + } + /* write LibData */ BLO_write_id_struct(writer, VFont, id_address, &vf->id); BKE_id_blend_write(writer, &vf->id); diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 28695d769d3..e6a67b191f8 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -460,31 +460,54 @@ GeometryComponent *InstancesComponent::copy() const new_component->positions_ = positions_; new_component->rotations_ = rotations_; new_component->scales_ = scales_; - new_component->objects_ = objects_; + new_component->instanced_data_ = instanced_data_; return new_component; } void InstancesComponent::clear() { - objects_.clear(); + instanced_data_.clear(); positions_.clear(); rotations_.clear(); scales_.clear(); } -void InstancesComponent::add_instance(const Object *object, + +void InstancesComponent::add_instance(Object *object, + blender::float3 position, + blender::float3 rotation, + blender::float3 scale) +{ + InstancedData data; + data.type = INSTANCE_DATA_TYPE_OBJECT; + data.data.object = object; + this->add_instance(data, position, rotation, scale); +} + +void InstancesComponent::add_instance(Collection *collection, + blender::float3 position, + blender::float3 rotation, + blender::float3 scale) +{ + InstancedData data; + data.type = INSTANCE_DATA_TYPE_COLLECTION; + data.data.collection = collection; + this->add_instance(data, position, rotation, scale); +} + +void InstancesComponent::add_instance(InstancedData data, blender::float3 position, blender::float3 rotation, blender::float3 scale) { - objects_.append(object); + instanced_data_.append(data); positions_.append(position); rotations_.append(rotation); scales_.append(scale); } -Span<const Object *> InstancesComponent::objects() const +Span<InstancedData> InstancesComponent::instanced_data() const { - return objects_; + return instanced_data_; } Span<float3> InstancesComponent::positions() const @@ -509,8 +532,11 @@ MutableSpan<float3> InstancesComponent::positions() int InstancesComponent::instances_amount() const { - BLI_assert(positions_.size() == objects_.size()); - return objects_.size(); + const int size = instanced_data_.size(); + BLI_assert(positions_.size() == size); + BLI_assert(rotations_.size() == size); + BLI_assert(scales_.size() == size); + return size; } bool InstancesComponent::is_empty() const @@ -538,7 +564,7 @@ int BKE_geometry_set_instances(const GeometrySet *geometry_set, float (**r_positions)[3], float (**r_rotations)[3], float (**r_scales)[3], - Object ***r_objects) + InstancedData **r_instanced_data) { const InstancesComponent *component = geometry_set->get_component_for_read<InstancesComponent>(); if (component == nullptr) { @@ -547,7 +573,7 @@ int BKE_geometry_set_instances(const GeometrySet *geometry_set, *r_positions = (float(*)[3])component->positions().data(); *r_rotations = (float(*)[3])component->rotations().data(); *r_scales = (float(*)[3])component->scales().data(); - *r_objects = (Object **)component->objects().data(); + *r_instanced_data = (InstancedData *)component->instanced_data().data(); return component->instances_amount(); } diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 8cc11468771..be14d74de7a 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -36,8 +36,11 @@ #include "BLT_translation.h" +#include "DNA_collection_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" #include "BKE_collection.h" #include "BKE_context.h" diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index fc74439fd76..981f5d50353 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -34,6 +34,7 @@ #include "BLI_blenlib.h" #include "BLI_ghash.h" #include "BLI_hash.h" +#include "BLI_heap.h" #include "BLI_math_vector.h" #include "BLI_polyfill_2d.h" @@ -41,6 +42,7 @@ #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_scene_types.h" @@ -3224,4 +3226,197 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a, gps_a, dvert, pt, delta, pt->pressure * ratio, pt->strength, deltatime); } } + +/* Stroke Uniform Subdivide ------------------------------------- */ + +typedef struct tSamplePoint { + struct tSamplePoint *next, *prev; + float x, y, z; + float pressure, strength, time; + float vertex_color[4]; + struct MDeformWeight *dw; + int totweight; +} tSamplePoint; + +typedef struct tSampleEdge { + float length_sq; + tSamplePoint *from; + tSamplePoint *to; +} tSampleEdge; + +/* Helper: creates a tSamplePoint from a bGPDspoint and (optionally) a MDeformVert. */ +static tSamplePoint *new_sample_point_from_gp_point(const bGPDspoint *pt, const MDeformVert *dvert) +{ + tSamplePoint *new_pt = MEM_callocN(sizeof(tSamplePoint), __func__); + copy_v3_v3(&new_pt->x, &pt->x); + new_pt->pressure = pt->pressure; + new_pt->strength = pt->strength; + new_pt->time = pt->time; + copy_v4_v4((float *)&new_pt->vertex_color, (float *)&pt->vert_color); + if (dvert != NULL) { + new_pt->totweight = dvert->totweight; + new_pt->dw = MEM_callocN(sizeof(MDeformWeight) * new_pt->totweight, __func__); + for (uint i = 0; i < new_pt->totweight; ++i) { + MDeformWeight *dw = &new_pt->dw[i]; + MDeformWeight *dw_from = &dvert->dw[i]; + dw->def_nr = dw_from->def_nr; + dw->weight = dw_from->weight; + } + } + return new_pt; +} + +/* Helper: creates a tSampleEdge from two tSamplePoints. Also calculates the length (squared) of + * the edge. */ +static tSampleEdge *new_sample_edge_from_sample_points(tSamplePoint *from, tSamplePoint *to) +{ + tSampleEdge *new_edge = MEM_callocN(sizeof(tSampleEdge), __func__); + new_edge->from = from; + new_edge->to = to; + new_edge->length_sq = len_squared_v3v3(&from->x, &to->x); + return new_edge; +} + +/** + * Subdivide the grease pencil stroke so the number of points is target_number. + * Does not change the shape of the stroke. The new points will be distributed as + * uniformly as possible by repeatedly subdividing the current longest edge. + * + * \param gps: The stroke to be up-sampled. + * \param target_number: The number of points the up-sampled stroke should have. + * \param select: Select/Deselect the stroke. + */ +void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd, + bGPDstroke *gps, + const uint32_t target_number, + const bool select) +{ + /* Stroke needs at least two points and strictly less points than the target number. */ + if (gps == NULL || gps->totpoints < 2 || gps->totpoints >= target_number) { + return; + } + + const int totpoints = gps->totpoints; + const bool has_dverts = (gps->dvert != NULL); + const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC); + + ListBase points = {NULL, NULL}; + Heap *edges = BLI_heap_new(); + + /* Add all points into list. */ + for (uint32_t i = 0; i < totpoints; ++i) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = has_dverts ? &gps->dvert[i] : NULL; + tSamplePoint *sp = new_sample_point_from_gp_point(pt, dvert); + BLI_addtail(&points, sp); + } + + /* Iterate over edges and insert them into the heap. */ + for (tSamplePoint *pt = ((tSamplePoint *)points.first)->next; pt != NULL; pt = pt->next) { + tSampleEdge *se = new_sample_edge_from_sample_points(pt->prev, pt); + /* BLI_heap is a min-heap, but we need the largest key to be at the top, so we take the + * negative of the squared length. */ + BLI_heap_insert(edges, -(se->length_sq), se); + } + + if (is_cyclic) { + tSamplePoint *sp_first = points.first; + tSamplePoint *sp_last = points.last; + tSampleEdge *se = new_sample_edge_from_sample_points(sp_last, sp_first); + BLI_heap_insert(edges, -(se->length_sq), se); + } + + int num_points_needed = target_number - totpoints; + BLI_assert(num_points_needed > 0); + + while (num_points_needed > 0) { + tSampleEdge *se = BLI_heap_pop_min(edges); + tSamplePoint *sp = se->from; + tSamplePoint *sp_next = se->to; + + /* Subdivide the edge. */ + tSamplePoint *new_sp = MEM_callocN(sizeof(tSamplePoint), __func__); + interp_v3_v3v3(&new_sp->x, &sp->x, &sp_next->x, 0.5f); + new_sp->pressure = interpf(sp->pressure, sp_next->pressure, 0.5f); + new_sp->strength = interpf(sp->strength, sp_next->strength, 0.5f); + new_sp->time = interpf(sp->time, sp_next->time, 0.5f); + interp_v4_v4v4((float *)&new_sp->vertex_color, + (float *)&sp->vertex_color, + (float *)&sp_next->vertex_color, + 0.5f); + if (sp->dw && sp_next->dw) { + new_sp->totweight = MIN2(sp->totweight, sp_next->totweight); + new_sp->dw = MEM_callocN(sizeof(MDeformWeight) * new_sp->totweight, __func__); + for (uint32_t i = 0; i < new_sp->totweight; ++i) { + MDeformWeight *dw = &new_sp->dw[i]; + MDeformWeight *dw_from = &sp->dw[i]; + MDeformWeight *dw_to = &sp_next->dw[i]; + dw->def_nr = dw_from->def_nr; + dw->weight = interpf(dw_from->weight, dw_to->weight, 0.5f); + } + } + BLI_insertlinkafter(&points, sp, new_sp); + + tSampleEdge *se_prev = new_sample_edge_from_sample_points(sp, new_sp); + tSampleEdge *se_next = new_sample_edge_from_sample_points(new_sp, sp_next); + BLI_heap_insert(edges, -(se_prev->length_sq), se_prev); + BLI_heap_insert(edges, -(se_next->length_sq), se_next); + + MEM_freeN(se); + num_points_needed--; + } + + /* Edges are no longer needed. Heap is freed. */ + BLI_heap_free(edges, (HeapFreeFP)MEM_freeN); + + gps->totpoints = target_number; + gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints); + if (has_dverts) { + gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints); + } + + /* Convert list back to stroke point array. */ + tSamplePoint *sp = points.first; + for (uint32_t i = 0; i < gps->totpoints && sp; ++i, sp = sp->next) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + copy_v3_v3(&pt->x, &sp->x); + pt->pressure = sp->pressure; + pt->strength = sp->strength; + pt->time = sp->time; + copy_v4_v4((float *)&pt->vert_color, (float *)&sp->vertex_color); + + if (sp->dw) { + dvert->totweight = sp->totweight; + dvert->dw = MEM_callocN(sizeof(MDeformWeight) * dvert->totweight, __func__); + for (uint32_t j = 0; j < dvert->totweight; ++j) { + MDeformWeight *dw = &dvert->dw[j]; + MDeformWeight *dw_from = &sp->dw[j]; + dw->def_nr = dw_from->def_nr; + dw->weight = dw_from->weight; + } + } + if (select) { + pt->flag |= GP_SPOINT_SELECT; + } + } + + if (select) { + gps->flag |= GP_STROKE_SELECT; + } + + /* Free the sample points. Important to use the mutable loop here because we are erasing the list + * elements. */ + LISTBASE_FOREACH_MUTABLE (tSamplePoint *, temp, &points) { + if (temp->dw != NULL) { + MEM_freeN(temp->dw); + } + MEM_SAFE_FREE(temp); + } + + /* Update the geometry of the stroke. */ + BKE_gpencil_stroke_geometry_update(gpd, gps); +} + /** \} */ diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index ac81e4a5470..1be2cba31b5 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -534,7 +534,7 @@ void BKE_gpencil_modifier_set_error(GpencilModifierData *md, const char *_format * Check whether given modifier is not local (i.e. from linked data) when the object is a library * override. * - * \param gmd May be NULL, in which case we consider it as a non-local modifier case. + * \param gmd: May be NULL, in which case we consider it as a non-local modifier case. */ bool BKE_gpencil_modifier_is_nonlocal_in_liboverride(const Object *ob, const GpencilModifierData *gmd) diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.cc index eec9013d067..fbf69357478 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.cc @@ -1,4 +1,4 @@ -/* +/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -21,9 +21,10 @@ * \ingroup bke */ -#include <math.h> -#include <stdlib.h> -#include <string.h> +#include <cmath> +#include <cstdlib> +#include <cstring> +#include <mutex> #include "CLG_log.h" @@ -46,6 +47,7 @@ #include "BLI_string.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_global.h" /* only for G.background test */ #include "BKE_icons.h" @@ -61,6 +63,8 @@ #include "BLO_read_write.h" +#include "atomic_ops.h" + /** * Only allow non-managed icons to be removed (by Python for eg). * Previews & ID's have their own functions to remove icons. @@ -73,28 +77,35 @@ enum { static CLG_LogRef LOG = {"bke.icons"}; -static GHash *gIcons = NULL; +/* Protected by gIconMutex. */ +static GHash *gIcons = nullptr; +/* Protected by gIconMutex. */ static int gNextIconId = 1; +/* Protected by gIconMutex. */ static int gFirstIconId = 1; -static GHash *gCachedPreviews = NULL; +std::mutex gIconMutex; + +/* Not mutex-protected! */ +static GHash *gCachedPreviews = nullptr; /* Queue of icons for deferred deletion. */ typedef struct DeferredIconDeleteNode { struct DeferredIconDeleteNode *next; int icon_id; } DeferredIconDeleteNode; +/* Protected by gIconMutex. */ static LockfreeLinkList g_icon_delete_queue; static void icon_free(void *val) { - Icon *icon = val; + Icon *icon = (Icon *)val; if (icon) { if (icon->obj_type == ICON_DATA_GEOM) { - struct Icon_Geom *obj = icon->obj; + struct Icon_Geom *obj = (struct Icon_Geom *)icon->obj; if (obj->mem) { /* coords & colors are part of this memory. */ MEM_freeN((void *)obj->mem); @@ -121,6 +132,12 @@ static void icon_free_data(int icon_id, Icon *icon) if (icon->obj_type == ICON_DATA_ID) { ((ID *)(icon->obj))->icon_id = 0; } + else if (icon->obj_type == ICON_DATA_IMBUF) { + ImBuf *imbuf = (ImBuf *)icon->obj; + if (imbuf) { + IMB_freeImBuf(imbuf); + } + } else if (icon->obj_type == ICON_DATA_PREVIEW) { ((PreviewImage *)(icon->obj))->icon_id = 0; } @@ -131,8 +148,8 @@ static void icon_free_data(int icon_id, Icon *icon) ((struct Icon_Geom *)(icon->obj))->icon_id = 0; } else if (icon->obj_type == ICON_DATA_STUDIOLIGHT) { - StudioLight *sl = icon->obj; - if (sl != NULL) { + StudioLight *sl = (StudioLight *)icon->obj; + if (sl != nullptr) { BKE_studiolight_unset_icon_id(sl, icon_id); } } @@ -141,19 +158,27 @@ static void icon_free_data(int icon_id, Icon *icon) } } +static Icon *icon_ghash_lookup(int icon_id) +{ + std::scoped_lock lock(gIconMutex); + return (Icon *)BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id)); +} + /* create an id for a new icon and make sure that ids from deleted icons get reused * after the integer number range is used up */ -static int get_next_free_id(void) +static int get_next_free_id() { - BLI_assert(BLI_thread_is_main()); + std::scoped_lock lock(gIconMutex); int startId = gFirstIconId; /* if we haven't used up the int number range, we just return the next int */ if (gNextIconId >= gFirstIconId) { - return gNextIconId++; + int next_id = gNextIconId++; + return next_id; } - /* now we try to find the smallest icon id not stored in the gIcons hash */ + /* Now we try to find the smallest icon id not stored in the gIcons hash. + * Don't use icon_ghash_lookup here, it would lock recursively (dead-lock). */ while (BLI_ghash_lookup(gIcons, POINTER_FROM_INT(startId)) && startId >= gFirstIconId) { startId++; } @@ -189,13 +214,13 @@ void BKE_icons_free(void) BLI_assert(BLI_thread_is_main()); if (gIcons) { - BLI_ghash_free(gIcons, NULL, icon_free); - gIcons = NULL; + BLI_ghash_free(gIcons, nullptr, icon_free); + gIcons = nullptr; } if (gCachedPreviews) { BLI_ghash_free(gCachedPreviews, MEM_freeN, BKE_previewimg_freefunc); - gCachedPreviews = NULL; + gCachedPreviews = nullptr; } BLI_linklist_lockfree_free(&g_icon_delete_queue, MEM_freeN); @@ -203,20 +228,21 @@ void BKE_icons_free(void) void BKE_icons_deferred_free(void) { - BLI_assert(BLI_thread_is_main()); + std::scoped_lock lock(gIconMutex); for (DeferredIconDeleteNode *node = (DeferredIconDeleteNode *)BLI_linklist_lockfree_begin(&g_icon_delete_queue); - node != NULL; + node != nullptr; node = node->next) { - BLI_ghash_remove(gIcons, POINTER_FROM_INT(node->icon_id), NULL, icon_free); + BLI_ghash_remove(gIcons, POINTER_FROM_INT(node->icon_id), nullptr, icon_free); } BLI_linklist_lockfree_clear(&g_icon_delete_queue, MEM_freeN); } static PreviewImage *previewimg_create_ex(size_t deferred_data_size) { - PreviewImage *prv_img = MEM_mallocN(sizeof(PreviewImage) + deferred_data_size, "img_prv"); + PreviewImage *prv_img = (PreviewImage *)MEM_mallocN(sizeof(PreviewImage) + deferred_data_size, + "img_prv"); memset(prv_img, 0, sizeof(*prv_img)); /* leave deferred data dirty */ if (deferred_data_size) { @@ -224,12 +250,26 @@ static PreviewImage *previewimg_create_ex(size_t deferred_data_size) } for (int i = 0; i < NUM_ICON_SIZES; i++) { - prv_img->flag[i] |= PRV_CHANGED; + prv_img->flag[i] |= (PRV_CHANGED | PRV_UNFINISHED); prv_img->changed_timestamp[i] = 0; } return prv_img; } +static PreviewImage *previewimg_deferred_create(const char *path, int source) +{ + /* We pack needed data for lazy loading (source type, in a single char, and path). */ + const size_t deferred_data_size = strlen(path) + 2; + char *deferred_data; + + PreviewImage *prv = previewimg_create_ex(deferred_data_size); + deferred_data = (char *)PRV_DEFERRED_DATA(prv); + deferred_data[0] = source; + memcpy(&deferred_data[1], path, deferred_data_size - 1); + + return prv; +} + PreviewImage *BKE_previewimg_create(void) { return previewimg_create_ex(0); @@ -256,7 +296,7 @@ void BKE_previewimg_free(PreviewImage **prv) { if (prv && (*prv)) { BKE_previewimg_freefunc(*prv); - *prv = NULL; + *prv = nullptr; } } @@ -267,7 +307,7 @@ void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size) GPU_texture_free(prv->gputexture[size]); } prv->h[size] = prv->w[size] = 0; - prv->flag[size] |= PRV_CHANGED; + prv->flag[size] |= (PRV_CHANGED | PRV_UNFINISHED); prv->flag[size] &= ~PRV_USER_EDITED; prv->changed_timestamp[size] = 0; } @@ -275,21 +315,21 @@ void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size) void BKE_previewimg_clear(struct PreviewImage *prv) { for (int i = 0; i < NUM_ICON_SIZES; i++) { - BKE_previewimg_clear_single(prv, i); + BKE_previewimg_clear_single(prv, (eIconSizes)i); } } PreviewImage *BKE_previewimg_copy(const PreviewImage *prv) { - PreviewImage *prv_img = NULL; + PreviewImage *prv_img = nullptr; if (prv) { - prv_img = MEM_dupallocN(prv); + prv_img = (PreviewImage *)MEM_dupallocN(prv); for (int i = 0; i < NUM_ICON_SIZES; i++) { if (prv->rect[i]) { - prv_img->rect[i] = MEM_dupallocN(prv->rect[i]); + prv_img->rect[i] = (uint *)MEM_dupallocN(prv->rect[i]); } - prv_img->gputexture[i] = NULL; + prv_img->gputexture[i] = nullptr; } } return prv_img; @@ -305,7 +345,7 @@ void BKE_previewimg_id_copy(ID *new_id, const ID *old_id) PreviewImage **new_prv_p = BKE_previewimg_id_get_p(new_id); if (old_prv_p && *old_prv_p) { - BLI_assert(new_prv_p != NULL && ELEM(*new_prv_p, NULL, *old_prv_p)); + BLI_assert(new_prv_p != nullptr && ELEM(*new_prv_p, nullptr, *old_prv_p)); // const int new_icon_id = get_next_free_id(); // if (new_icon_id == 0) { @@ -339,7 +379,13 @@ PreviewImage **BKE_previewimg_id_get_p(const ID *id) break; } - return NULL; + return nullptr; +} + +PreviewImage *BKE_previewimg_id_get(const ID *id) +{ + PreviewImage **prv_p = BKE_previewimg_id_get_p(id); + return prv_p ? *prv_p : nullptr; } void BKE_previewimg_id_free(ID *id) @@ -355,18 +401,60 @@ PreviewImage *BKE_previewimg_id_ensure(ID *id) PreviewImage **prv_p = BKE_previewimg_id_get_p(id); if (prv_p) { - if (*prv_p == NULL) { + if (*prv_p == nullptr) { *prv_p = BKE_previewimg_create(); } return *prv_p; } - return NULL; + return nullptr; +} + +void BKE_previewimg_id_custom_set(ID *id, const char *path) +{ + PreviewImage **prv = BKE_previewimg_id_get_p(id); + + /* Thumbnail previews must use the deferred pipeline. But we force them to be immediately + * generated here still. */ + + if (*prv) { + BKE_previewimg_deferred_release(*prv); + } + *prv = previewimg_deferred_create(path, THB_SOURCE_IMAGE); + + /* Can't lazy-render the preview on access. ID previews are saved to files and we want them to be + * there in time. Not only if something happened to have accessed it meanwhile. */ + for (int i = 0; i < NUM_ICON_SIZES; i++) { + BKE_previewimg_ensure(*prv, i); + /* Prevent auto-updates. */ + (*prv)->flag[i] |= PRV_USER_EDITED; + } +} + +bool BKE_previewimg_id_supports_jobs(const ID *id) +{ + return ELEM(GS(id->name), ID_OB, ID_MA, ID_TE, ID_LA, ID_WO, ID_IM, ID_BR); +} + +void BKE_previewimg_deferred_release(PreviewImage *prv) +{ + if (prv) { + if (prv->tag & PRV_TAG_DEFFERED_RENDERING) { + /* We cannot delete the preview while it is being loaded in another thread... */ + prv->tag |= PRV_TAG_DEFFERED_DELETE; + return; + } + if (prv->icon_id) { + BKE_icon_delete(prv->icon_id); + } + BKE_previewimg_freefunc(prv); + } } PreviewImage *BKE_previewimg_cached_get(const char *name) { - return BLI_ghash_lookup(gCachedPreviews, name); + BLI_assert(BLI_thread_is_main()); + return (PreviewImage *)BLI_ghash_lookup(gCachedPreviews, name); } /** @@ -374,14 +462,16 @@ PreviewImage *BKE_previewimg_cached_get(const char *name) */ PreviewImage *BKE_previewimg_cached_ensure(const char *name) { - PreviewImage *prv = NULL; + BLI_assert(BLI_thread_is_main()); + + PreviewImage *prv = nullptr; void **key_p, **prv_p; if (!BLI_ghash_ensure_p_ex(gCachedPreviews, name, &key_p, &prv_p)) { *key_p = BLI_strdup(name); *prv_p = BKE_previewimg_create(); } - prv = *prv_p; + prv = *(PreviewImage **)prv_p; BLI_assert(prv); return prv; @@ -389,24 +479,27 @@ PreviewImage *BKE_previewimg_cached_ensure(const char *name) /** * Generate a PreviewImage from given file path, using thumbnails management, if not yet existing. + * Does not actually generate the preview, #BKE_previewimg_ensure() must be called for that. */ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, const char *path, const int source, bool force_update) { - PreviewImage *prv = NULL; + BLI_assert(BLI_thread_is_main()); + + PreviewImage *prv = nullptr; void **prv_p; prv_p = BLI_ghash_lookup_p(gCachedPreviews, name); if (prv_p) { - prv = *prv_p; + prv = *(PreviewImage **)prv_p; BLI_assert(prv); } if (prv && force_update) { - const char *prv_deferred_data = PRV_DEFERRED_DATA(prv); + const char *prv_deferred_data = (char *)PRV_DEFERRED_DATA(prv); if (((int)prv_deferred_data[0] == source) && STREQ(&prv_deferred_data[1], path)) { /* If same path, no need to re-allocate preview, just clear it up. */ BKE_previewimg_clear(prv); @@ -417,15 +510,7 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, } if (!prv) { - /* We pack needed data for lazy loading (source type, in a single char, and path). */ - const size_t deferred_data_size = strlen(path) + 2; - char *deferred_data; - - prv = previewimg_create_ex(deferred_data_size); - deferred_data = PRV_DEFERRED_DATA(prv); - deferred_data[0] = source; - memcpy(&deferred_data[1], path, deferred_data_size - 1); - + prv = previewimg_deferred_create(path, source); force_update = true; } @@ -441,26 +526,13 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, return prv; } -void BKE_previewimg_cached_release_pointer(PreviewImage *prv) -{ - if (prv) { - if (prv->tag & PRV_TAG_DEFFERED_RENDERING) { - /* We cannot delete the preview while it is being loaded in another thread... */ - prv->tag |= PRV_TAG_DEFFERED_DELETE; - return; - } - if (prv->icon_id) { - BKE_icon_delete(prv->icon_id); - } - BKE_previewimg_freefunc(prv); - } -} - void BKE_previewimg_cached_release(const char *name) { - PreviewImage *prv = BLI_ghash_popkey(gCachedPreviews, name, MEM_freeN); + BLI_assert(BLI_thread_is_main()); + + PreviewImage *prv = (PreviewImage *)BLI_ghash_popkey(gCachedPreviews, name, MEM_freeN); - BKE_previewimg_cached_release_pointer(prv); + BKE_previewimg_deferred_release(prv); } /** @@ -475,12 +547,12 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size) if (do_icon || do_preview) { ImBuf *thumb; - char *prv_deferred_data = PRV_DEFERRED_DATA(prv); + char *prv_deferred_data = (char *)PRV_DEFERRED_DATA(prv); int source = prv_deferred_data[0]; char *path = &prv_deferred_data[1]; int icon_w, icon_h; - thumb = IMB_thumb_manage(path, THB_LARGE, source); + thumb = IMB_thumb_manage(path, THB_LARGE, (ThumbSource)source); if (thumb) { /* PreviewImage assumes premultiplied alhpa... */ @@ -489,8 +561,8 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size) if (do_preview) { prv->w[ICON_SIZE_PREVIEW] = thumb->x; prv->h[ICON_SIZE_PREVIEW] = thumb->y; - prv->rect[ICON_SIZE_PREVIEW] = MEM_dupallocN(thumb->rect); - prv->flag[ICON_SIZE_PREVIEW] &= ~(PRV_CHANGED | PRV_USER_EDITED); + prv->rect[ICON_SIZE_PREVIEW] = (uint *)MEM_dupallocN(thumb->rect); + prv->flag[ICON_SIZE_PREVIEW] &= ~(PRV_CHANGED | PRV_USER_EDITED | PRV_UNFINISHED); } if (do_icon) { if (thumb->x > thumb->y) { @@ -508,8 +580,8 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size) IMB_scaleImBuf(thumb, icon_w, icon_h); prv->w[ICON_SIZE_ICON] = icon_w; prv->h[ICON_SIZE_ICON] = icon_h; - prv->rect[ICON_SIZE_ICON] = MEM_dupallocN(thumb->rect); - prv->flag[ICON_SIZE_ICON] &= ~(PRV_CHANGED | PRV_USER_EDITED); + prv->rect[ICON_SIZE_ICON] = (uint *)MEM_dupallocN(thumb->rect); + prv->flag[ICON_SIZE_ICON] &= ~(PRV_CHANGED | PRV_USER_EDITED | PRV_UNFINISHED); } IMB_freeImBuf(thumb); } @@ -517,13 +589,45 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size) } } +/** + * Create an #ImBuf holding a copy of the preview image buffer in \a prv. + * \note The returned image buffer has to be free'd (#IMB_freeImBuf()). + */ +ImBuf *BKE_previewimg_to_imbuf(PreviewImage *prv, const int size) +{ + const unsigned int w = prv->w[size]; + const unsigned int h = prv->h[size]; + const unsigned int *rect = prv->rect[size]; + + ImBuf *ima = nullptr; + + if (w > 0 && h > 0 && rect) { + /* first allocate imbuf for copying preview into it */ + ima = IMB_allocImBuf(w, h, 32, IB_rect); + memcpy(ima->rect, rect, w * h * sizeof(*ima->rect)); + } + + return ima; +} + +void BKE_previewimg_finish(PreviewImage *prv, const int size) +{ + /* Previews may be calculated on a thread. */ + atomic_fetch_and_and_int16(&prv->flag[size], ~PRV_UNFINISHED); +} + +bool BKE_previewimg_is_finished(const PreviewImage *prv, const int size) +{ + return (prv->flag[size] & PRV_UNFINISHED) == 0; +} + void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv) { /* Note we write previews also for undo steps. It takes up some memory, * but not doing so would causes all previews to be re-rendered after * undo which is too expensive. */ - if (prv == NULL) { + if (prv == nullptr) { return; } @@ -532,7 +636,7 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv) if (!(U.flag & USER_SAVE_PREVIEWS)) { prv_copy.w[1] = 0; prv_copy.h[1] = 0; - prv_copy.rect[1] = NULL; + prv_copy.rect[1] = nullptr; } BLO_write_struct_at_address(writer, PreviewImage, prv, &prv_copy); if (prv_copy.rect[0]) { @@ -545,7 +649,7 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv) void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv) { - if (prv == NULL) { + if (prv == nullptr) { return; } @@ -553,7 +657,18 @@ void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv) if (prv->rect[i]) { BLO_read_data_address(reader, &prv->rect[i]); } - prv->gputexture[i] = NULL; + prv->gputexture[i] = nullptr; + /* For now consider previews read from file as finished to not confuse File Browser preview + * loading. That could be smarter and check if there's a preview job running instead. + * If the preview is tagged as changed, it needs to be updated anyway, so don't remove the tag. + */ + if ((prv->flag[i] & PRV_CHANGED) == 0) { + BKE_previewimg_finish(prv, i); + } + else { + /* Only for old files that didn't write the flag . */ + prv->flag[i] |= PRV_UNFINISHED; + } } prv->icon_id = 0; prv->tag = 0; @@ -561,15 +676,13 @@ void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv) void BKE_icon_changed(const int icon_id) { - BLI_assert(BLI_thread_is_main()); - - Icon *icon = NULL; + Icon *icon = nullptr; if (!icon_id || G.background) { return; } - icon = BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id)); + icon = icon_ghash_lookup(icon_id); if (icon) { /* We *only* expect ID-tied icons here, not non-ID icon/preview! */ @@ -584,7 +697,7 @@ void BKE_icon_changed(const int icon_id) /* If we have previews, they all are now invalid changed. */ if (p_prv && *p_prv) { for (int i = 0; i < NUM_ICON_SIZES; i++) { - (*p_prv)->flag[i] |= PRV_CHANGED; + (*p_prv)->flag[i] |= (PRV_CHANGED | PRV_UNFINISHED); (*p_prv)->changed_timestamp[i]++; } } @@ -593,7 +706,7 @@ void BKE_icon_changed(const int icon_id) static Icon *icon_create(int icon_id, int obj_type, void *obj) { - Icon *new_icon = MEM_mallocN(sizeof(Icon), __func__); + Icon *new_icon = (Icon *)MEM_mallocN(sizeof(Icon), __func__); new_icon->obj_type = obj_type; new_icon->obj = obj; @@ -601,10 +714,13 @@ static Icon *icon_create(int icon_id, int obj_type, void *obj) new_icon->flag = 0; /* next two lines make sure image gets created */ - new_icon->drawinfo = NULL; - new_icon->drawinfo_free = NULL; + new_icon->drawinfo = nullptr; + new_icon->drawinfo_free = nullptr; - BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), new_icon); + { + std::scoped_lock lock(gIconMutex); + BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), new_icon); + } return new_icon; } @@ -731,17 +847,47 @@ int BKE_icon_preview_ensure(ID *id, PreviewImage *preview) return preview->icon_id; } +/** + * Create an icon as owner or \a ibuf. The icon-ID is not stored in \a ibuf, it needs to be stored + * separately. + * \note Transforms ownership of \a ibuf to the newly created icon. + */ +int BKE_icon_imbuf_create(ImBuf *ibuf) +{ + int icon_id = get_next_free_id(); + + Icon *icon = icon_create(icon_id, ICON_DATA_IMBUF, ibuf); + icon->flag = ICON_FLAG_MANAGED; + + return icon_id; +} + +ImBuf *BKE_icon_imbuf_get_buffer(int icon_id) +{ + Icon *icon = icon_ghash_lookup(icon_id); + if (!icon) { + CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id); + return nullptr; + } + if (icon->obj_type != ICON_DATA_IMBUF) { + CLOG_ERROR(&LOG, "icon ID does not refer to an imbuf icon: %d", icon_id); + return nullptr; + } + + return (ImBuf *)icon->obj; +} + Icon *BKE_icon_get(const int icon_id) { BLI_assert(BLI_thread_is_main()); - Icon *icon = NULL; + Icon *icon = nullptr; - icon = BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id)); + icon = icon_ghash_lookup(icon_id); if (!icon) { CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id); - return NULL; + return nullptr; } return icon; @@ -749,10 +895,9 @@ Icon *BKE_icon_get(const int icon_id) void BKE_icon_set(const int icon_id, struct Icon *icon) { - BLI_assert(BLI_thread_is_main()); - void **val_p; + std::scoped_lock lock(gIconMutex); if (BLI_ghash_ensure_p(gIcons, POINTER_FROM_INT(icon_id), &val_p)) { CLOG_ERROR(&LOG, "icon already set: %d", icon_id); return; @@ -763,8 +908,10 @@ void BKE_icon_set(const int icon_id, struct Icon *icon) static void icon_add_to_deferred_delete_queue(int icon_id) { - DeferredIconDeleteNode *node = MEM_mallocN(sizeof(DeferredIconDeleteNode), __func__); + DeferredIconDeleteNode *node = (DeferredIconDeleteNode *)MEM_mallocN( + sizeof(DeferredIconDeleteNode), __func__); node->icon_id = icon_id; + /* Doesn't need lock. */ BLI_linklist_lockfree_insert(&g_icon_delete_queue, (LockfreeLinkNode *)node); } @@ -782,7 +929,8 @@ void BKE_icon_id_delete(struct ID *id) } BKE_icons_deferred_free(); - BLI_ghash_remove(gIcons, POINTER_FROM_INT(icon_id), NULL, icon_free); + std::scoped_lock lock(gIconMutex); + BLI_ghash_remove(gIcons, POINTER_FROM_INT(icon_id), nullptr, icon_free); } /** @@ -795,8 +943,8 @@ bool BKE_icon_delete(const int icon_id) return false; } - Icon *icon = BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL); - if (icon) { + std::scoped_lock lock(gIconMutex); + if (Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), nullptr)) { icon_free_data(icon_id, icon); icon_free(icon); return true; @@ -812,7 +960,9 @@ bool BKE_icon_delete_unmanaged(const int icon_id) return false; } - Icon *icon = BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL); + std::scoped_lock lock(gIconMutex); + + Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), nullptr); if (icon) { if (UNLIKELY(icon->flag & ICON_FLAG_MANAGED)) { BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), icon); @@ -847,52 +997,52 @@ int BKE_icon_geom_ensure(struct Icon_Geom *geom) return geom->icon_id; } -struct Icon_Geom *BKE_icon_geom_from_memory(const uchar *data, size_t data_len) +struct Icon_Geom *BKE_icon_geom_from_memory(uchar *data, size_t data_len) { BLI_assert(BLI_thread_is_main()); if (data_len <= 8) { - goto fail; + return nullptr; } + /* Wrapper for RAII early exit cleanups. */ + std::unique_ptr<uchar> data_wrapper(std::move(data)); + /* Skip the header. */ data_len -= 8; const int div = 3 * 2 * 3; const int coords_len = data_len / div; if (coords_len * div != data_len) { - goto fail; + return nullptr; } const uchar header[4] = {'V', 'C', 'O', 0}; - const uchar *p = data; + uchar *p = data_wrapper.get(); if (memcmp(p, header, ARRAY_SIZE(header)) != 0) { - goto fail; + return nullptr; } p += 4; - struct Icon_Geom *geom = MEM_mallocN(sizeof(*geom), __func__); + struct Icon_Geom *geom = (struct Icon_Geom *)MEM_mallocN(sizeof(*geom), __func__); geom->coords_range[0] = (int)*p++; geom->coords_range[1] = (int)*p++; /* x, y ignored for now */ p += 2; geom->coords_len = coords_len; - geom->coords = (void *)p; - geom->colors = (void *)(p + (data_len / 3)); + geom->coords = reinterpret_cast<decltype(geom->coords)>(p); + geom->colors = reinterpret_cast<decltype(geom->colors)>(p + (data_len / 3)); geom->icon_id = 0; - geom->mem = data; + /* Move buffer ownership to C buffer. */ + geom->mem = data_wrapper.release(); return geom; - -fail: - MEM_freeN((void *)data); - return NULL; } struct Icon_Geom *BKE_icon_geom_from_file(const char *filename) { BLI_assert(BLI_thread_is_main()); size_t data_len; - uchar *data = BLI_file_read_binary_as_mem(filename, 0, &data_len); - if (data == NULL) { - return NULL; + uchar *data = (uchar *)BLI_file_read_binary_as_mem(filename, 0, &data_len); + if (data == nullptr) { + return nullptr; } return BKE_icon_geom_from_memory(data, data_len); } diff --git a/source/blender/blenkernel/intern/idprop_utils.c b/source/blender/blenkernel/intern/idprop_utils.c index f8a1113f69b..433f0e97844 100644 --- a/source/blender/blenkernel/intern/idprop_utils.c +++ b/source/blender/blenkernel/intern/idprop_utils.c @@ -26,6 +26,8 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "DNA_ID.h" + #include "BKE_idprop.h" #include "BKE_idtype.h" diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index 44bf8f0e4db..1889d1c4eb0 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -36,6 +36,7 @@ #include "BLT_translation.h" #include "DNA_ID.h" +#include "DNA_collection_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index cd2ed32cd4f..228aed265cf 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -54,6 +54,7 @@ #include "DNA_camera_types.h" #include "DNA_defaults.h" #include "DNA_light_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -225,14 +226,21 @@ static void image_foreach_cache(ID *id, static void image_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Image *ima = (Image *)id; - if (ima->id.us > 0 || BLO_write_is_undo(writer)) { + const bool is_undo = BLO_write_is_undo(writer); + if (ima->id.us > 0 || is_undo) { ImagePackedFile *imapf; - /* Some trickery to keep forward compatibility of packed images. */ BLI_assert(ima->packedfile == NULL); - if (ima->packedfiles.first != NULL) { - imapf = ima->packedfiles.first; - ima->packedfile = imapf->packedfile; + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(ima) && !is_undo) { + BLI_listbase_clear(&ima->packedfiles); + } + else { + /* Some trickery to keep forward compatibility of packed images. */ + if (ima->packedfiles.first != NULL) { + imapf = ima->packedfiles.first; + ima->packedfile = imapf->packedfile; + } } /* write LibData */ diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c index 9ed233ab34c..50138b34fa3 100644 --- a/source/blender/blenkernel/intern/image_gpu.c +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -62,7 +62,7 @@ bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf) return ibuf->rect_float != NULL; } } - else if (ibuf) { + if (ibuf) { if (ibuf->rect_float) { return image ? (image->alpha_mode != IMA_ALPHA_STRAIGHT) : false; } diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 6a852df95c6..0e611b21304 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -467,7 +467,9 @@ static char *shapekey_adrcodes_to_paths(ID *id, int adrcode, int *UNUSED(array_i /* setting that we alter is the "value" (i.e. keyblock.curval) */ if (kb) { /* Use the keyblock name, escaped, so that path lookups for this will work */ - BLI_snprintf(buf, sizeof(buf), "key_blocks[\"%s\"].value", kb->name); + char kb_name_esc[sizeof(kb->name) * 2]; + BLI_str_escape(kb_name_esc, kb->name, sizeof(kb_name_esc)); + BLI_snprintf(buf, sizeof(buf), "key_blocks[\"%s\"].value", kb_name_esc); } else { /* Fallback - Use the adrcode as index directly, so that this can be manually fixed */ @@ -1118,7 +1120,7 @@ static char *get_rna_access(ID *id, propname = "speed_fader"; break; case SEQ_FAC_OPACITY: - propname = "blend_opacity"; + propname = "blend_alpha"; break; } /* XXX this doesn't seem to be included anywhere in sequencer RNA... */ @@ -1160,7 +1162,12 @@ static char *get_rna_access(ID *id, /* note, strings are not escapted and they should be! */ if ((actname && actname[0]) && (constname && constname[0])) { /* Constraint in Pose-Channel */ - BLI_snprintf(buf, sizeof(buf), "pose.bones[\"%s\"].constraints[\"%s\"]", actname, constname); + char actname_esc[sizeof(((bActionChannel *)NULL)->name) * 2]; + char constname_esc[sizeof(((bConstraint *)NULL)->name) * 2]; + BLI_str_escape(actname_esc, actname, sizeof(actname_esc)); + BLI_str_escape(constname_esc, constname, sizeof(constname_esc)); + BLI_snprintf( + buf, sizeof(buf), "pose.bones[\"%s\"].constraints[\"%s\"]", actname_esc, constname_esc); } else if (actname && actname[0]) { if ((blocktype == ID_OB) && STREQ(actname, "Object")) { @@ -1174,16 +1181,22 @@ static char *get_rna_access(ID *id, } else { /* Pose-Channel */ - BLI_snprintf(buf, sizeof(buf), "pose.bones[\"%s\"]", actname); + char actname_esc[sizeof(((bActionChannel *)NULL)->name) * 2]; + BLI_str_escape(actname_esc, actname, sizeof(actname_esc)); + BLI_snprintf(buf, sizeof(buf), "pose.bones[\"%s\"]", actname_esc); } } else if (constname && constname[0]) { /* Constraint in Object */ - BLI_snprintf(buf, sizeof(buf), "constraints[\"%s\"]", constname); + char constname_esc[sizeof(((bConstraint *)NULL)->name) * 2]; + BLI_str_escape(constname_esc, constname, sizeof(constname_esc)); + BLI_snprintf(buf, sizeof(buf), "constraints[\"%s\"]", constname_esc); } else if (seq) { /* Sequence names in Scene */ - BLI_snprintf(buf, sizeof(buf), "sequence_editor.sequences_all[\"%s\"]", seq->name + 2); + char seq_name_esc[(sizeof(seq->name) - 2) * 2]; + BLI_str_escape(seq_name_esc, seq->name + 2, sizeof(seq_name_esc)); + BLI_snprintf(buf, sizeof(buf), "sequence_editor.sequences_all[\"%s\"]", seq_name_esc); } else { buf[0] = '\0'; /* empty string */ diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 7468112b40e..433d64a5927 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -108,7 +108,8 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data) static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Key *key = (Key *)id; - if (key->id.us > 0 || BLO_write_is_undo(writer)) { + const bool is_undo = BLO_write_is_undo(writer); + if (key->id.us > 0 || is_undo) { /* write LibData */ BLO_write_id_struct(writer, Key, id_address, &key->id); BKE_id_blend_write(writer, &key->id); @@ -119,9 +120,15 @@ static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_add /* direct data */ LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { - BLO_write_struct(writer, KeyBlock, kb); - if (kb->data) { - BLO_write_raw(writer, kb->totelem * key->elemsize, kb->data); + KeyBlock tmp_kb = *kb; + /* Do not store actual geometry data in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(key) && !is_undo) { + tmp_kb.totelem = 0; + tmp_kb.data = NULL; + } + BLO_write_struct_at_address(writer, KeyBlock, kb, &tmp_kb); + if (tmp_kb.data != NULL) { + BLO_write_raw(writer, tmp_kb.totelem * key->elemsize, tmp_kb.data); } } } diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 8a699e31f37..fbad0d920ed 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -2054,7 +2054,7 @@ static void bke_view_layer_verify_aov_cb(void *userdata, const char *name, int UNUSED(channels), const char *UNUSED(chanid), - int UNUSED(type)) + eNodeSocketDatatype UNUSED(type)) { GHash *name_count = userdata; void **value_p; diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 21a77e2b45a..be7ce34f7e6 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -37,6 +37,7 @@ /* all types are needed here, in order to do memory operations */ #include "DNA_ID.h" #include "DNA_anim_types.h" +#include "DNA_collection_types.h" #include "DNA_gpencil_types.h" #include "DNA_key_types.h" #include "DNA_node_types.h" @@ -55,6 +56,7 @@ #include "BKE_anim_data.h" #include "BKE_armature.h" +#include "BKE_asset.h" #include "BKE_bpath.h" #include "BKE_context.h" #include "BKE_global.h" @@ -2264,6 +2266,12 @@ bool BKE_id_is_in_global_main(ID *id) return (id == NULL || BLI_findindex(which_libbase(G_MAIN, GS(id->name)), id) != -1); } +bool BKE_id_can_be_asset(const ID *id) +{ + return !ID_IS_LINKED(id) && !ID_IS_OVERRIDE_LIBRARY(id) && + BKE_idtype_idcode_is_linkable(GS(id->name)); +} + /************************* Datablock order in UI **************************/ static int *id_order_get(ID *id) @@ -2361,6 +2369,10 @@ void BKE_id_reorder(const ListBase *lb, ID *id, ID *relative, bool after) void BKE_id_blend_write(BlendWriter *writer, ID *id) { + if (id->asset_data) { + BKE_asset_metadata_write(writer, id->asset_data); + } + /* ID_WM's id->properties are considered runtime only, and never written in .blend file. */ if (id->properties && !ELEM(GS(id->name), ID_WM)) { IDP_BlendWrite(writer, id->properties); diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index 1e45a3c1163..7199bd0f13c 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -31,6 +31,7 @@ #include "BLI_listbase.h" #include "BKE_anim_data.h" +#include "BKE_asset.h" #include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_key.h" @@ -64,6 +65,10 @@ void BKE_libblock_free_data(ID *id, const bool do_id_user) id->override_library = NULL; } + if (id->asset_data) { + BKE_asset_metadata_free(&id->asset_data); + } + BKE_animdata_free(id, do_id_user); } diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index d82315a0e7f..cabc80d4024 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -369,6 +369,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint tag, + const uint missing_tag, Library *override_group_lib_reference) { void **entry_vp = BLI_ghash_lookup_p(bmain->relations->id_user_to_used, id); @@ -377,9 +378,16 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, return (id->tag & tag) != 0; } + /* Note: in case some reference ID is missing from linked data (and therefore its override uses + * a placeholder as reference), use `missing_tag` instead of `tag` for this override. */ if (override_group_lib_reference != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(id) && id->override_library->reference->lib == override_group_lib_reference) { - id->tag |= tag; + if (id->override_library->reference->tag & LIB_TAG_MISSING) { + id->tag |= missing_tag; + } + else { + id->tag |= tag; + } } /* This way we won't process again that ID, should we encounter it again through another @@ -397,7 +405,7 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, /* We only consider IDs from the same library. */ if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) { if (lib_override_hierarchy_recursive_tag( - bmain, *entry->id_pointer, tag, override_group_lib_reference) && + bmain, *entry->id_pointer, tag, missing_tag, override_group_lib_reference) && override_group_lib_reference == NULL) { id->tag |= tag; } @@ -430,7 +438,7 @@ void BKE_lib_override_library_dependencies_tag(Main *bmain, /* We tag all intermediary data-blocks in-between two overridden ones (e.g. if a shape-key * has a driver using an armature object's bone, we need to override the shape-key/obdata, * the objects using them, etc.) */ - lib_override_hierarchy_recursive_tag(bmain, id_root, tag, NULL); + lib_override_hierarchy_recursive_tag(bmain, id_root, tag, 0, NULL); BKE_main_relations_free(bmain); } @@ -447,6 +455,7 @@ void BKE_lib_override_library_dependencies_tag(Main *bmain, void BKE_lib_override_library_override_group_tag(Main *bmain, ID *id_root, const uint tag, + const uint missing_tag, const bool do_create_main_relashionships) { if (do_create_main_relashionships) { @@ -456,7 +465,7 @@ void BKE_lib_override_library_override_group_tag(Main *bmain, /* We tag all liboverride data-blocks from the same library as reference one, * being used by the root ID. */ lib_override_hierarchy_recursive_tag( - bmain, id_root, tag, id_root->override_library->reference->lib); + bmain, id_root, tag, missing_tag, id_root->override_library->reference->lib); BKE_main_relations_free(bmain); } @@ -492,8 +501,9 @@ static int lib_override_library_make_tag_ids_cb(LibraryIDLinkCallbackData *cb_da } /* We tag all collections and objects for override. And we also tag all other data-blocks which - * would use one of those. */ - if (ELEM(GS(id->name), ID_OB, ID_GR)) { + * would use one of those. + * Note: missing IDs (aka placeholders) are never overridden. */ + if (ELEM(GS(id->name), ID_OB, ID_GR) && !(id->tag & LIB_TAG_MISSING)) { id->tag |= LIB_TAG_DOIT; } @@ -692,6 +702,12 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, &ob_proxy->proxy->id; ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; + /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not + * sure this is a valid state, but for now just abort the overriding process. */ + if (!ID_IS_OVERRIDABLE_LIBRARY(id_root)) { + return false; + } + /* We manually convert the proxy object into a library override, further override handling will * then be handled by `BKE_lib_override_library_create()` just as for a regular override * creation. @@ -725,7 +741,7 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, and tag * linked reference ones to be overridden again. */ - BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true); + BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_MISSING, true); GHash *linkedref_to_old_override = BLI_ghash_new( BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); @@ -835,6 +851,12 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ } id->tag &= ~LIB_TAG_DOIT; } + /* Also cleanup old overrides that went missing in new linked data. */ + else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY(id)); + id->tag |= LIB_TAG_DOIT; + id->tag &= ~LIB_TAG_MISSING; + } } FOREACH_MAIN_ID_END; BKE_id_multi_tagged_delete(bmain); @@ -876,7 +898,7 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) id_root->tag |= LIB_TAG_DOIT; /* Tag all library overrides in the chains of dependencies from the given root one. */ - BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true); + BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_DOIT, true); ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 9e3189afee9..56f7bb0be6f 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -24,6 +24,7 @@ #include "BLI_utildefines.h" +#include "DNA_collection_types.h" #include "DNA_object_types.h" #include "BKE_armature.h" @@ -342,7 +343,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id) { /* Update all group nodes using a node group. */ - ntreeUpdateAllUsers(bmain, (bNodeTree *)new_id); + ntreeUpdateAllUsers(bmain, new_id); } /** diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 9ccdf5189d1..53f2a85fdad 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -173,24 +173,53 @@ static void mesh_foreach_id(ID *id, LibraryForeachIDData *data) static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Mesh *mesh = (Mesh *)id; - if (mesh->id.us > 0 || BLO_write_is_undo(writer)) { - /* cache only - don't write */ - mesh->mface = NULL; - mesh->totface = 0; - memset(&mesh->fdata, 0, sizeof(mesh->fdata)); - memset(&mesh->runtime, 0, sizeof(mesh->runtime)); - + const bool is_undo = BLO_write_is_undo(writer); + if (mesh->id.us > 0 || is_undo) { CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE]; CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE]; CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE]; CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE]; CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_blend_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff)); - CustomData_blend_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff)); + /* cache only - don't write */ + mesh->mface = NULL; + mesh->totface = 0; + memset(&mesh->fdata, 0, sizeof(mesh->fdata)); + memset(&mesh->runtime, 0, sizeof(mesh->runtime)); flayers = flayers_buff; - CustomData_blend_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); - CustomData_blend_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + + /* Do not store actual geometry data in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) { + mesh->mvert = NULL; + mesh->totvert = 0; + memset(&mesh->vdata, 0, sizeof(mesh->vdata)); + vlayers = vlayers_buff; + + mesh->medge = NULL; + mesh->totedge = 0; + memset(&mesh->edata, 0, sizeof(mesh->edata)); + elayers = elayers_buff; + + mesh->mloop = NULL; + mesh->totloop = 0; + memset(&mesh->ldata, 0, sizeof(mesh->ldata)); + llayers = llayers_buff; + + mesh->mpoly = NULL; + mesh->totpoly = 0; + memset(&mesh->pdata, 0, sizeof(mesh->pdata)); + players = players_buff; + } + else { + CustomData_blend_write_prepare( + &mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff)); + CustomData_blend_write_prepare( + &mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff)); + CustomData_blend_write_prepare( + &mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); + CustomData_blend_write_prepare( + &mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + } BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); BKE_id_blend_write(writer, &mesh->id); diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index 8272bd07411..73883afe19e 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -1460,7 +1460,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, return result; } -/* This is a Mesh-based copy of the same function in DerivedMesh.c */ +/* This is a Mesh-based copy of the same function in DerivedMesh.cc */ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int actshape_uid) { KeyBlock *kb; diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc new file mode 100644 index 00000000000..527288d06cf --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -0,0 +1,505 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing": + * https://github.com/fedackb/mesh-fairing. + */ + +/** \file + * \ingroup bke + */ + +#include "BLI_map.hh" +#include "BLI_math.h" +#include "BLI_vector.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_mesh.h" +#include "BKE_mesh_fair.h" +#include "BKE_mesh_mapping.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "MEM_guardedalloc.h" +#include "eigen_capi.h" + +using blender::Map; +using blender::Vector; +using std::array; + +class VertexWeight { + public: + virtual float weight_at_index(const int index) = 0; + virtual ~VertexWeight() = default; +}; + +class LoopWeight { + public: + virtual float weight_at_index(const int index) = 0; + virtual ~LoopWeight() = default; +}; + +class FairingContext { + public: + /* Get coordinates of vertices which are adjacent to the loop with specified index. */ + virtual void adjacents_coords_from_loop(const int loop, + float r_adj_next[3], + float r_adj_prev[3]) = 0; + + /* Get the other vertex index for a loop. */ + virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) = 0; + + int vertex_count_get() + { + return totvert_; + } + + int loop_count_get() + { + return totvert_; + } + + MeshElemMap *vertex_loop_map_get(const int v) + { + return &vlmap_[v]; + } + + float *vertex_deformation_co_get(const int v) + { + return co_[v]; + } + + virtual ~FairingContext() = default; + + void fair_vertices(bool *affected, + const eMeshFairingDepth depth, + VertexWeight *vertex_weight, + LoopWeight *loop_weight) + { + + fair_vertices_ex(affected, (int)depth, vertex_weight, loop_weight); + } + + protected: + Vector<float *> co_; + + int totvert_; + int totloop_; + + MeshElemMap *vlmap_; + int *vlmap_mem_; + + private: + void fair_setup_fairing(const int v, + const int i, + LinearSolver *solver, + float multiplier, + const int depth, + Map<int, int> &vert_col_map, + VertexWeight *vertex_weight, + LoopWeight *loop_weight) + { + if (depth == 0) { + if (vert_col_map.contains(v)) { + const int j = vert_col_map.lookup(v); + EIG_linear_solver_matrix_add(solver, i, j, -multiplier); + return; + } + for (int j = 0; j < 3; j++) { + EIG_linear_solver_right_hand_side_add(solver, j, i, multiplier * co_[v][j]); + } + return; + } + + float w_ij_sum = 0; + const float w_i = vertex_weight->weight_at_index(v); + MeshElemMap *vlmap_elem = &vlmap_[v]; + for (int l = 0; l < vlmap_elem->count; l++) { + const int l_index = vlmap_elem->indices[l]; + const int other_vert = other_vertex_index_from_loop(l_index, v); + const float w_ij = loop_weight->weight_at_index(l_index); + w_ij_sum += w_ij; + fair_setup_fairing(other_vert, + i, + solver, + w_i * w_ij * multiplier, + depth - 1, + vert_col_map, + vertex_weight, + loop_weight); + } + fair_setup_fairing(v, + i, + solver, + -1 * w_i * w_ij_sum * multiplier, + depth - 1, + vert_col_map, + vertex_weight, + loop_weight); + } + + void fair_vertices_ex(bool *affected, + const int order, + VertexWeight *vertex_weight, + LoopWeight *loop_weight) + { + Map<int, int> vert_col_map; + int num_affected_vertices = 0; + for (int i = 0; i < totvert_; i++) { + if (!affected[i]) { + continue; + } + vert_col_map.add(i, num_affected_vertices); + num_affected_vertices++; + } + + /* Early return, nothing to do. */ + if (num_affected_vertices == 0 || num_affected_vertices == totvert_) { + return; + } + + /* Setup fairing matrices */ + LinearSolver *solver = EIG_linear_solver_new(num_affected_vertices, num_affected_vertices, 3); + for (auto item : vert_col_map.items()) { + const int v = item.key; + const int col = item.value; + fair_setup_fairing(v, col, solver, 1.0f, order, vert_col_map, vertex_weight, loop_weight); + } + + /* Solve linear system */ + EIG_linear_solver_solve(solver); + + /* Copy the result back to the mesh */ + for (auto item : vert_col_map.items()) { + const int v = item.key; + const int col = item.value; + for (int j = 0; j < 3; j++) { + co_[v][j] = EIG_linear_solver_variable_get(solver, j, col); + } + } + + /* Free solver data */ + EIG_linear_solver_delete(solver); + } +}; + +class MeshFairingContext : public FairingContext { + public: + MeshFairingContext(Mesh *mesh, MVert *deform_mverts) + { + totvert_ = mesh->totvert; + totloop_ = mesh->totloop; + + medge_ = mesh->medge; + mpoly_ = mesh->mpoly; + mloop_ = mesh->mloop; + BKE_mesh_vert_loop_map_create(&vlmap_, + &vlmap_mem_, + mesh->mpoly, + mesh->mloop, + mesh->totvert, + mesh->totpoly, + mesh->totloop); + + /* Deformation coords. */ + co_.reserve(mesh->totvert); + if (deform_mverts) { + for (int i = 0; i < mesh->totvert; i++) { + co_[i] = deform_mverts[i].co; + } + } + else { + for (int i = 0; i < mesh->totvert; i++) { + co_[i] = mesh->mvert[i].co; + } + } + + loop_to_poly_map_.reserve(mesh->totloop); + for (int i = 0; i < mesh->totpoly; i++) { + for (int l = 0; l < mesh->mpoly[i].totloop; l++) { + loop_to_poly_map_[l + mesh->mpoly[i].loopstart] = i; + } + } + } + + ~MeshFairingContext() + { + MEM_SAFE_FREE(vlmap_); + MEM_SAFE_FREE(vlmap_mem_); + } + + virtual void adjacents_coords_from_loop(const int loop, + float r_adj_next[3], + float r_adj_prev[3]) override + { + const int vert = mloop_[loop].v; + const MPoly *p = &mpoly_[loop_to_poly_map_[loop]]; + const int corner = poly_find_loop_from_vert(p, &mloop_[p->loopstart], vert); + copy_v3_v3(r_adj_next, co_[ME_POLY_LOOP_NEXT(mloop_, p, corner)->v]); + copy_v3_v3(r_adj_prev, co_[ME_POLY_LOOP_PREV(mloop_, p, corner)->v]); + } + + virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) override + { + MEdge *e = &medge_[mloop_[loop].e]; + if (e->v1 == v) { + return e->v2; + } + return e->v1; + } + + protected: + Mesh *mesh_; + MLoop *mloop_; + MPoly *mpoly_; + MEdge *medge_; + Vector<int> loop_to_poly_map_; +}; + +class BMeshFairingContext : public FairingContext { + public: + BMeshFairingContext(BMesh *bm) + { + this->bm = bm; + totvert_ = bm->totvert; + totloop_ = bm->totloop; + + BM_mesh_elem_table_ensure(bm, BM_VERT); + BM_mesh_elem_index_ensure(bm, BM_LOOP); + + /* Deformation coords. */ + co_.reserve(bm->totvert); + for (int i = 0; i < bm->totvert; i++) { + BMVert *v = BM_vert_at_index(bm, i); + co_[i] = v->co; + } + + bmloop_.reserve(bm->totloop); + vlmap_ = (MeshElemMap *)MEM_calloc_arrayN(sizeof(MeshElemMap), bm->totvert, "bmesh loop map"); + vlmap_mem_ = (int *)MEM_malloc_arrayN(sizeof(int), bm->totloop, "bmesh loop map mempool"); + + BMVert *v; + BMLoop *l; + BMIter iter; + BMIter loop_iter; + int index_iter = 0; + + /* This initializes both the bmloop and the vlmap for bmesh in a single loop. */ + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + int loop_count = 0; + const int vert_index = BM_elem_index_get(v); + vlmap_[vert_index].indices = &vlmap_mem_[index_iter]; + BM_ITER_ELEM (l, &loop_iter, v, BM_LOOPS_OF_VERT) { + const int loop_index = BM_elem_index_get(l); + bmloop_[loop_index] = l; + vlmap_mem_[index_iter] = loop_index; + index_iter++; + loop_count++; + } + vlmap_[vert_index].count = loop_count; + } + } + + ~BMeshFairingContext() + { + MEM_SAFE_FREE(vlmap_); + MEM_SAFE_FREE(vlmap_mem_); + } + + virtual void adjacents_coords_from_loop(const int loop, + float r_adj_next[3], + float r_adj_prev[3]) override + { + copy_v3_v3(r_adj_next, bmloop_[loop]->next->v->co); + copy_v3_v3(r_adj_prev, bmloop_[loop]->prev->v->co); + } + + virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) override + { + BMLoop *l = bmloop_[loop]; + BMVert *bmvert = BM_vert_at_index(bm, v); + BMVert *bm_other_vert = BM_edge_other_vert(l->e, bmvert); + return BM_elem_index_get(bm_other_vert); + } + + protected: + BMesh *bm; + Vector<BMLoop *> bmloop_; +}; + +class UniformVertexWeight : public VertexWeight { + public: + UniformVertexWeight(FairingContext *fairing_context) + { + const int totvert = fairing_context->vertex_count_get(); + vertex_weights_.reserve(totvert); + for (int i = 0; i < totvert; i++) { + const int tot_loop = fairing_context->vertex_loop_map_get(i)->count; + if (tot_loop != 0) { + vertex_weights_[i] = 1.0f / tot_loop; + } + else { + vertex_weights_[i] = FLT_MAX; + } + } + } + ~UniformVertexWeight() = default; + + float weight_at_index(const int index) override + { + return vertex_weights_[index]; + } + + private: + Vector<float> vertex_weights_; +}; + +class VoronoiVertexWeight : public VertexWeight { + + public: + VoronoiVertexWeight(FairingContext *fairing_context) + { + + const int totvert = fairing_context->vertex_count_get(); + vertex_weights_.reserve(totvert); + for (int i = 0; i < totvert; i++) { + + float area = 0.0f; + float a[3]; + copy_v3_v3(a, fairing_context->vertex_deformation_co_get(i)); + const float acute_threshold = M_PI_2; + + MeshElemMap *vlmap_elem = fairing_context->vertex_loop_map_get(i); + for (int l = 0; l < vlmap_elem->count; l++) { + const int l_index = vlmap_elem->indices[l]; + + float b[3], c[3], d[3]; + fairing_context->adjacents_coords_from_loop(l_index, b, c); + + if (angle_v3v3v3(c, fairing_context->vertex_deformation_co_get(i), b) < acute_threshold) { + calc_circumcenter(d, a, b, c); + } + else { + add_v3_v3v3(d, b, c); + mul_v3_fl(d, 0.5f); + } + + float t[3]; + add_v3_v3v3(t, a, b); + mul_v3_fl(t, 0.5f); + area += area_tri_v3(a, t, d); + + add_v3_v3v3(t, a, c); + mul_v3_fl(t, 0.5f); + area += area_tri_v3(a, d, t); + } + + vertex_weights_[i] = area != 0.0f ? 1.0f / area : 1e12; + } + } + ~VoronoiVertexWeight() = default; + + float weight_at_index(const int index) override + { + return vertex_weights_[index]; + } + + private: + Vector<float> vertex_weights_; + + void calc_circumcenter(float r[3], const float a[3], const float b[3], const float c[3]) + { + float ab[3]; + sub_v3_v3v3(ab, b, a); + + float ac[3]; + sub_v3_v3v3(ac, c, a); + + float ab_cross_ac[3]; + cross_v3_v3v3(ab_cross_ac, ab, ac); + + if (len_squared_v3(ab_cross_ac) > 0.0f) { + float d[3]; + cross_v3_v3v3(d, ab_cross_ac, ab); + mul_v3_fl(d, len_squared_v3(ac)); + + float t[3]; + cross_v3_v3v3(t, ac, ab_cross_ac); + mul_v3_fl(t, len_squared_v3(ab)); + + add_v3_v3(d, t); + + mul_v3_fl(d, 1.0f / (2.0f * len_squared_v3(ab_cross_ac))); + + add_v3_v3v3(r, a, d); + return; + } + copy_v3_v3(r, a); + } +}; + +class UniformLoopWeight : public LoopWeight { + public: + float weight_at_index(const int UNUSED(index)) override + { + return 1.0f; + } +}; + +static void prefair_and_fair_vertices(FairingContext *fairing_context, + bool *affected_vertices, + const eMeshFairingDepth depth) +{ + /* Prefair. */ + UniformVertexWeight *uniform_vertex_weights = new UniformVertexWeight(fairing_context); + UniformLoopWeight *uniform_loop_weights = new UniformLoopWeight(); + fairing_context->fair_vertices( + affected_vertices, depth, uniform_vertex_weights, uniform_loop_weights); + delete uniform_vertex_weights; + + /* Fair. */ + VoronoiVertexWeight *voronoi_vertex_weights = new VoronoiVertexWeight(fairing_context); + /* TODO: Implemente cotangent loop weights. */ + fairing_context->fair_vertices( + affected_vertices, depth, voronoi_vertex_weights, uniform_loop_weights); + + delete uniform_loop_weights; + delete voronoi_vertex_weights; +} + +void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh, + struct MVert *deform_mverts, + bool *affect_vertices, + const eMeshFairingDepth depth) +{ + MeshFairingContext *fairing_context = new MeshFairingContext(mesh, deform_mverts); + prefair_and_fair_vertices(fairing_context, affect_vertices, depth); + delete fairing_context; +} + +void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm, + bool *affect_vertices, + const eMeshFairingDepth depth) +{ + BMeshFairingContext *fairing_context = new BMeshFairingContext(bm); + prefair_and_fair_vertices(fairing_context, affect_vertices, depth); + delete fairing_context; +} diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 3496e05c6e5..ba0f59f6363 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -38,6 +38,7 @@ #include "DNA_armature_types.h" #include "DNA_cloth_types.h" +#include "DNA_dynamicpaint_types.h" #include "DNA_fluid_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_mesh_types.h" @@ -578,7 +579,7 @@ bool BKE_modifier_is_enabled(const struct Scene *scene, ModifierData *md, int re * Check whether given modifier is not local (i.e. from linked data) when the object is a library * override. * - * \param md May be NULL, in which case we consider it as a non-local modifier case. + * \param md: May be NULL, in which case we consider it as a non-local modifier case. */ bool BKE_modifier_is_nonlocal_in_liboverride(const Object *ob, const ModifierData *md) { diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.h b/source/blender/blenkernel/intern/multires_unsubdivide.h index 39c6da0b6c8..0a03387282f 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.h +++ b/source/blender/blenkernel/intern/multires_unsubdivide.h @@ -26,10 +26,8 @@ #include "BLI_sys_types.h" struct BMesh; -struct Depsgraph; struct Mesh; struct MultiresModifierData; -struct Object; typedef struct MultiresUnsubdivideGrid { /* For sanity checks. */ diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index ebd9317fcf1..75230f9045c 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -334,11 +334,8 @@ NlaStrip *BKE_nlastrip_new(bAction *act) /* generic settings * - selected flag to highlight this to the user * - (XXX) disabled Auto-Blends, as this was often causing some unwanted effects - * - (XXX) synchronization of strip-length in accordance with changes to action-length - * is not done though, since this should only really happens in editmode for strips now - * though this decision is still subject to further review... */ - strip->flag = NLASTRIP_FLAG_SELECT; + strip->flag = NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_SYNC_LENGTH; /* assign the action reference */ strip->act = act; @@ -1195,7 +1192,7 @@ bool BKE_nlatrack_get_bounds(NlaTrack *nlt, float bounds[2]) * Check whether given NLA track is not local (i.e. from linked data) when the object is a library * override. * - * \param nlt May be NULL, in which case we consider it as a non-local track case. + * \param nlt: May be NULL, in which case we consider it as a non-local track case. */ bool BKE_nlatrack_is_nonlocal_in_liboverride(const ID *id, const NlaTrack *nlt) { diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 31de95817fd..415eb14be66 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -35,6 +35,7 @@ #include "DNA_action_types.h" #include "DNA_anim_types.h" +#include "DNA_collection_types.h" #include "DNA_gpencil_types.h" #include "DNA_light_types.h" #include "DNA_linestyle_types.h" @@ -59,6 +60,7 @@ #include "BKE_anim_data.h" #include "BKE_animsys.h" #include "BKE_colortools.h" +#include "BKE_cryptomatte.h" #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_idtype.h" @@ -274,6 +276,11 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); break; } + case SOCK_COLLECTION: { + bNodeSocketValueCollection *default_value = sock->default_value; + BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + break; + } case SOCK_FLOAT: case SOCK_VECTOR: case SOCK_RGBA: @@ -373,6 +380,9 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so case SOCK_IMAGE: BLO_write_struct(writer, bNodeSocketValueImage, sock->default_value); break; + case SOCK_COLLECTION: + BLO_write_struct(writer, bNodeSocketValueCollection, sock->default_value); + break; case __SOCK_MESH: case SOCK_CUSTOM: case SOCK_SHADER: @@ -482,10 +492,18 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) } else if ((ntree->type == NTREE_COMPOSIT) && (node->type == CMP_NODE_CRYPTOMATTE)) { NodeCryptomatte *nc = (NodeCryptomatte *)node->storage; + /* Update the matte_id so the files can be opened in versions that don't + * use `CryptomatteEntry`. */ + MEM_SAFE_FREE(nc->matte_id); + nc->matte_id = BKE_cryptomatte_entries_to_matte_id(nc); if (nc->matte_id) { BLO_write_string(writer, nc->matte_id); } + LISTBASE_FOREACH (CryptomatteEntry *, entry, &nc->entries) { + BLO_write_struct(writer, CryptomatteEntry, entry); + } BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage); + MEM_SAFE_FREE(nc->matte_id); } else if (node->typeinfo != &NodeTypeUndefined) { BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage); @@ -637,6 +655,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) case CMP_NODE_CRYPTOMATTE: { NodeCryptomatte *nc = (NodeCryptomatte *)node->storage; BLO_read_data_address(reader, &nc->matte_id); + BLO_read_list(reader, &nc->entries); break; } case TEX_NODE_IMAGE: { @@ -709,6 +728,11 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock BLO_read_id_address(reader, lib, &default_value->value); break; } + case SOCK_COLLECTION: { + bNodeSocketValueImage *default_value = sock->default_value; + BLO_read_id_address(reader, lib, &default_value->value); + break; + } case SOCK_FLOAT: case SOCK_VECTOR: case SOCK_RGBA: @@ -788,6 +812,11 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock) BLO_expand(expander, default_value->value); break; } + case SOCK_COLLECTION: { + bNodeSocketValueCollection *default_value = sock->default_value; + BLO_expand(expander, default_value->value); + break; + } case SOCK_FLOAT: case SOCK_VECTOR: case SOCK_RGBA: @@ -1345,6 +1374,11 @@ static void socket_id_user_increment(bNodeSocket *sock) id_us_plus((ID *)default_value->value); break; } + case SOCK_COLLECTION: { + bNodeSocketValueCollection *default_value = sock->default_value; + id_us_plus((ID *)default_value->value); + break; + } case SOCK_FLOAT: case SOCK_VECTOR: case SOCK_RGBA: @@ -1372,6 +1406,11 @@ static void socket_id_user_decrement(bNodeSocket *sock) id_us_min(&default_value->value->id); break; } + case SOCK_COLLECTION: { + bNodeSocketValueCollection *default_value = sock->default_value; + id_us_min(&default_value->value->id); + break; + } case SOCK_FLOAT: case SOCK_VECTOR: case SOCK_RGBA: @@ -1511,6 +1550,8 @@ const char *nodeStaticSocketType(int type, int subtype) return "NodeSocketImage"; case SOCK_GEOMETRY: return "NodeSocketGeometry"; + case SOCK_COLLECTION: + return "NodeSocketCollection"; } return NULL; } @@ -1578,6 +1619,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype) return "NodeSocketInterfaceImage"; case SOCK_GEOMETRY: return "NodeSocketInterfaceGeometry"; + case SOCK_COLLECTION: + return "NodeSocketInterfaceCollection"; } return NULL; } @@ -3960,9 +4003,9 @@ void ntreeUpdateAllNew(Main *main) FOREACH_NODETREE_END; } -void ntreeUpdateAllUsers(Main *main, bNodeTree *ngroup) +void ntreeUpdateAllUsers(Main *main, ID *id) { - if (ngroup == NULL) { + if (id == NULL) { return; } @@ -3971,7 +4014,7 @@ void ntreeUpdateAllUsers(Main *main, bNodeTree *ngroup) bool need_update = false; LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->id == &ngroup->id) { + if (node->id == id) { if (node->typeinfo->group_update_func) { node->typeinfo->group_update_func(ntree, node); } @@ -3986,13 +4029,16 @@ void ntreeUpdateAllUsers(Main *main, bNodeTree *ngroup) } FOREACH_NODETREE_END; - if (ngroup->type == NTREE_GEOMETRY) { - LISTBASE_FOREACH (Object *, object, &main->objects) { - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - if (md->type == eModifierType_Nodes) { - NodesModifierData *nmd = (NodesModifierData *)md; - if (nmd->node_group == ngroup) { - MOD_nodes_update_interface(object, nmd); + if (GS(id->name) == ID_NT) { + bNodeTree *ngroup = (bNodeTree *)id; + if (ngroup->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH (Object *, object, &main->objects) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group == ngroup) { + MOD_nodes_update_interface(object, nmd); + } } } } @@ -4041,7 +4087,7 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) } if (bmain) { - ntreeUpdateAllUsers(bmain, ntree); + ntreeUpdateAllUsers(bmain, &ntree->id); } if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) { @@ -4682,6 +4728,7 @@ static void registerGeometryNodes(void) { register_node_type_geo_group(); + register_node_type_geo_attribute_fill(); register_node_type_geo_triangulate(); register_node_type_geo_edge_split(); register_node_type_geo_transform(); @@ -4690,9 +4737,11 @@ static void registerGeometryNodes(void) register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); register_node_type_geo_object_info(); - register_node_type_geo_random_attribute(); + register_node_type_geo_attribute_randomize(); register_node_type_geo_attribute_math(); register_node_type_geo_join_geometry(); + register_node_type_geo_attribute_mix(); + register_node_type_geo_attribute_color_ramp(); } static void registerFunctionNodes(void) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index d747da2213e..8764232e56b 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -38,6 +38,7 @@ #include "DNA_collection_types.h" #include "DNA_constraint_types.h" #include "DNA_defaults.h" +#include "DNA_dynamicpaint_types.h" #include "DNA_effect_types.h" #include "DNA_fluid_types.h" #include "DNA_gpencil_modifier_types.h" @@ -1325,7 +1326,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); } if (ob->type == OB_POINTCLOUD) { - return (mti->modifyPointCloud != NULL); + return (mti->modifyGeometrySet != NULL); } if (ob->type == OB_VOLUME) { return (mti->modifyVolume != NULL); @@ -2208,7 +2209,9 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f BLI_listbase_clear(&psysn->childcachebufs); if (flag & LIB_ID_CREATE_NO_MAIN) { - BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0); + /* XXX Disabled, fails when evaluating depsgraph after copying ID with no main for preview + * creation. */ + // BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0); psysn->flag |= PSYS_SHARED_CACHES; BLI_assert(psysn->pointcache != NULL); } @@ -5217,8 +5220,11 @@ bool BKE_object_modifier_use_time(Object *ob, ModifierData *md) AnimData *adt = ob->adt; FCurve *fcu; - char pattern[MAX_NAME + 16]; - BLI_snprintf(pattern, sizeof(pattern), "modifiers[\"%s\"]", md->name); + char md_name_esc[sizeof(md->name) * 2]; + BLI_str_escape(md_name_esc, md->name, sizeof(md_name_esc)); + + char pattern[sizeof(md_name_esc) + 16]; + BLI_snprintf(pattern, sizeof(pattern), "modifiers[\"%s\"]", md_name_esc); /* action - check for F-Curves with paths containing 'modifiers[' */ if (adt->action) { @@ -5260,8 +5266,11 @@ bool BKE_object_modifier_gpencil_use_time(Object *ob, GpencilModifierData *md) AnimData *adt = ob->adt; FCurve *fcu; - char pattern[MAX_NAME + 32]; - BLI_snprintf(pattern, sizeof(pattern), "grease_pencil_modifiers[\"%s\"]", md->name); + char md_name_esc[sizeof(md->name) * 2]; + BLI_str_escape(md_name_esc, md->name, sizeof(md_name_esc)); + + char pattern[sizeof(md_name_esc) + 32]; + BLI_snprintf(pattern, sizeof(pattern), "grease_pencil_modifiers[\"%s\"]", md_name_esc); /* action - check for F-Curves with paths containing 'grease_pencil_modifiers[' */ if (adt->action) { @@ -5295,8 +5304,11 @@ bool BKE_object_shaderfx_use_time(Object *ob, ShaderFxData *fx) AnimData *adt = ob->adt; FCurve *fcu; - char pattern[MAX_NAME + 32]; - BLI_snprintf(pattern, sizeof(pattern), "shader_effects[\"%s\"]", fx->name); + char fx_name_esc[sizeof(fx->name) * 2]; + BLI_str_escape(fx_name_esc, fx->name, sizeof(fx_name_esc)); + + char pattern[sizeof(fx_name_esc) + 32]; + BLI_snprintf(pattern, sizeof(pattern), "shader_effects[\"%s\"]", fx_name_esc); /* action - check for F-Curves with paths containing string[' */ if (adt->action) { diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index d5434710e23..8fa708f31cb 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -816,15 +816,13 @@ static void make_duplis_instances_component(const DupliContext *ctx) float(*positions)[3]; float(*rotations)[3]; float(*scales)[3]; - Object **objects; + InstancedData *instanced_data; const int amount = BKE_geometry_set_instances( - ctx->object->runtime.geometry_set_eval, &positions, &rotations, &scales, &objects); + ctx->object->runtime.geometry_set_eval, &positions, &rotations, &scales, &instanced_data); for (int i = 0; i < amount; i++) { - Object *object = objects[i]; - if (object == NULL) { - continue; - } + InstancedData *data = &instanced_data[i]; + float scale_matrix[4][4]; size_to_mat4(scale_matrix, scales[i]); float rotation_matrix[4][4]; @@ -833,14 +831,43 @@ static void make_duplis_instances_component(const DupliContext *ctx) mul_m4_m4m4(instance_offset_matrix, rotation_matrix, scale_matrix); copy_v3_v3(instance_offset_matrix[3], positions[i]); - float matrix[4][4]; - mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrix); - make_dupli(ctx, object, matrix, i); + if (data->type == INSTANCE_DATA_TYPE_OBJECT) { + Object *object = data->data.object; + if (object != NULL) { + float matrix[4][4]; + mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrix); + make_dupli(ctx, object, matrix, i); + + float space_matrix[4][4]; + mul_m4_m4m4(space_matrix, instance_offset_matrix, object->imat); + mul_m4_m4_pre(space_matrix, ctx->object->obmat); + make_recursive_duplis(ctx, object, space_matrix, i); + } + } + else if (data->type == INSTANCE_DATA_TYPE_COLLECTION) { + Collection *collection = data->data.collection; + if (collection != NULL) { + float collection_matrix[4][4]; + unit_m4(collection_matrix); + sub_v3_v3(collection_matrix[3], collection->instance_offset); + mul_m4_m4_pre(collection_matrix, instance_offset_matrix); + mul_m4_m4_pre(collection_matrix, ctx->object->obmat); + + eEvaluationMode mode = DEG_get_mode(ctx->depsgraph); + FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, object, mode) { + if (object == ctx->object) { + continue; + } + + float instance_matrix[4][4]; + mul_m4_m4m4(instance_matrix, collection_matrix, object->obmat); - float space_matrix[4][4]; - mul_m4_m4m4(space_matrix, instance_offset_matrix, object->imat); - mul_m4_m4_pre(space_matrix, ctx->object->obmat); - make_recursive_duplis(ctx, object, space_matrix, i); + make_dupli(ctx, object, instance_matrix, i); + make_recursive_duplis(ctx, object, collection_matrix, i); + } + FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; + } + } } } diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 915ee56e2f7..1b4dc98252a 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -28,6 +28,7 @@ #include "DNA_key_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" #include "DNA_scene_types.h" #include "BLI_blenlib.h" diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index d516de535f9..dce45f44583 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -41,6 +41,7 @@ #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_force_types.h" #include "DNA_particle_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c index 98a55c3de95..6e0965650d3 100644 --- a/source/blender/blenkernel/intern/particle_child.c +++ b/source/blender/blenkernel/intern/particle_child.c @@ -25,6 +25,7 @@ #include "BLI_noise.h" #include "DNA_material_types.h" +#include "DNA_object_types.h" #include "BKE_colortools.h" #include "BKE_particle.h" diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 194593be4ff..c3cc9136057 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -40,6 +40,7 @@ #include "DNA_particle_types.h" #include "DNA_scene_types.h" +#include "BKE_customdata.h" #include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_mesh.h" diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 35265cf8b68..71df28c8b42 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -34,6 +34,7 @@ #include "DNA_anim_types.h" #include "DNA_boid_types.h" +#include "DNA_cloth_types.h" #include "DNA_curve_types.h" #include "DNA_listBase.h" #include "DNA_mesh_types.h" @@ -4978,6 +4979,24 @@ void particle_system_update(struct Depsgraph *depsgraph, /* ID looper */ +/* unfortunately PSys and modifier ID loopers are not directly compatible, so we need this struct + * and the callback below to map the former to the latter (thanks to psys embedding a Cloth + * modifier data struct now, for Hair physics simulations). */ +typedef struct ParticleSystemIDLoopForModifier { + ParticleSystem *psys; + ParticleSystemIDFunc func; + void *userdata; +} ParticleSystemIDLoopForModifier; + +static void particlesystem_modifiersForeachIDLink(void *user_data, + Object *UNUSED(object), + ID **id_pointer, + int cb_flag) +{ + ParticleSystemIDLoopForModifier *data = (ParticleSystemIDLoopForModifier *)user_data; + data->func(data->psys, id_pointer, data->userdata, cb_flag); +} + void BKE_particlesystem_id_loop(ParticleSystem *psys, ParticleSystemIDFunc func, void *userdata) { ParticleTarget *pt; @@ -4986,6 +5005,16 @@ void BKE_particlesystem_id_loop(ParticleSystem *psys, ParticleSystemIDFunc func, func(psys, (ID **)&psys->target_ob, userdata, IDWALK_CB_NOP); func(psys, (ID **)&psys->parent, userdata, IDWALK_CB_NOP); + if (psys->clmd != NULL) { + const ModifierTypeInfo *mti = BKE_modifier_get_info(psys->clmd->modifier.type); + + if (mti->foreachIDLink != NULL) { + ParticleSystemIDLoopForModifier data = {.psys = psys, .func = func, .userdata = userdata}; + mti->foreachIDLink( + &psys->clmd->modifier, NULL, particlesystem_modifiersForeachIDLink, &data); + } + } + for (pt = psys->targets.first; pt; pt = pt->next) { func(psys, (ID **)&pt->ob, userdata, IDWALK_CB_NOP); } diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 5f6685817b9..7bd14e80333 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -365,12 +365,31 @@ static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph, continue; } - if (mti->modifyPointCloud) { - mti->modifyPointCloud(md, &mectx, &geometry_set); + if (mti->modifyGeometrySet) { + mti->modifyGeometrySet(md, &mectx, &geometry_set); } } } +static PointCloud *take_pointcloud_ownership_from_geometry_set(GeometrySet &geometry_set) +{ + if (!geometry_set.has<PointCloudComponent>()) { + return nullptr; + } + PointCloudComponent &pointcloud_component = + geometry_set.get_component_for_write<PointCloudComponent>(); + PointCloud *pointcloud = pointcloud_component.release(); + if (pointcloud != nullptr) { + /* Add back, but as read-only non-owning component. */ + pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly); + } + else { + /* The component was empty, we can also remove it. */ + geometry_set.remove<PointCloudComponent>(); + } + return pointcloud; +} + void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) { /* Free any evaluated data and restore original data. */ @@ -382,10 +401,17 @@ void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene GeometryOwnershipType::ReadOnly); pointcloud_evaluate_modifiers(depsgraph, scene, object, geometry_set); + PointCloud *pointcloud_eval = take_pointcloud_ownership_from_geometry_set(geometry_set); + + /* If the geometry set did not contain a point cloud, we still create an empty one. */ + if (pointcloud_eval == nullptr) { + pointcloud_eval = BKE_pointcloud_new_nomain(0); + } + /* Assign evaluated object. */ - PointCloud *dummy_pointcloud = BKE_pointcloud_new_nomain(0); - BKE_object_eval_assign_data(object, &dummy_pointcloud->id, true); - object->runtime.geometry_set_eval = new GeometrySet(geometry_set); + const bool eval_is_owned = pointcloud_eval != pointcloud; + BKE_object_eval_assign_data(object, &pointcloud_eval->id, eval_is_owned); + object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set)); } /* Draw Cache */ diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c new file mode 100644 index 00000000000..8dcf6de164a --- /dev/null +++ b/source/blender/blenkernel/intern/preferences.c @@ -0,0 +1,119 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + * + * User defined asset library API. + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_string_utf8.h" +#include "BLI_string_utils.h" + +#include "BKE_appdir.h" +#include "BKE_preferences.h" + +#include "BLT_translation.h" + +#include "DNA_userdef_types.h" + +#define U BLI_STATIC_ASSERT(false, "Global 'U' not allowed, only use arguments passed in!") + +/* -------------------------------------------------------------------- */ +/** \name Asset Libraries + * \{ */ + +bUserAssetLibrary *BKE_preferences_asset_library_add(UserDef *userdef, + const char *name, + const char *path) +{ + bUserAssetLibrary *library = MEM_callocN(sizeof(*library), "bUserAssetLibrary"); + + BLI_addtail(&userdef->asset_libraries, library); + + if (name) { + BKE_preferences_asset_library_name_set(userdef, library, name); + } + if (path) { + BLI_strncpy(library->path, path, sizeof(library->path)); + } + + return library; +} + +void BKE_preferences_asset_library_name_set(UserDef *userdef, + bUserAssetLibrary *library, + const char *name) +{ + BLI_strncpy_utf8(library->name, name, sizeof(library->name)); + BLI_uniquename(&userdef->asset_libraries, + library, + name, + '.', + offsetof(bUserAssetLibrary, name), + sizeof(library->name)); +} + +/** + * Unlink and free a library preference member. + * \note Free's \a library itself. + */ +void BKE_preferences_asset_library_remove(UserDef *userdef, bUserAssetLibrary *library) +{ + BLI_freelinkN(&userdef->asset_libraries, library); +} + +bUserAssetLibrary *BKE_preferences_asset_library_find_from_index(const UserDef *userdef, int index) +{ + return BLI_findlink(&userdef->asset_libraries, index); +} + +bUserAssetLibrary *BKE_preferences_asset_library_find_from_name(const UserDef *userdef, + const char *name) +{ + return BLI_findstring(&userdef->asset_libraries, name, offsetof(bUserAssetLibrary, name)); +} + +int BKE_preferences_asset_library_get_index(const UserDef *userdef, + const bUserAssetLibrary *library) +{ + return BLI_findindex(&userdef->asset_libraries, library); +} + +void BKE_preferences_asset_library_default_add(UserDef *userdef) +{ + char documents_path[FILE_MAXDIR]; + + /* No home or documents path found, not much we can do. */ + if (!BKE_appdir_folder_documents(documents_path) || !documents_path[0]) { + return; + } + + bUserAssetLibrary *library = BKE_preferences_asset_library_add(userdef, DATA_("Default"), NULL); + + /* Add new "Default" library under '[doc_path]/Blender/Assets'. */ + BLI_path_join( + library->path, sizeof(library->path), documents_path, N_("Blender"), N_("Assets"), NULL); +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index bed10df5ace..cc192c1c3c0 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -37,6 +37,7 @@ #include "DNA_gpencil_types.h" #include "DNA_linestyle_types.h" #include "DNA_mask_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" @@ -47,6 +48,7 @@ #include "DNA_sound_types.h" #include "DNA_space_types.h" #include "DNA_text_types.h" +#include "DNA_vfont_types.h" #include "DNA_view3d_types.h" #include "DNA_windowmanager_types.h" #include "DNA_workspace_types.h" @@ -220,6 +222,7 @@ static void scene_init_data(ID *id) /* Curve Profile */ scene->toolsettings->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE); + scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init(); for (size_t i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) { scene->orientation_slots[i].index_custom = -1; @@ -860,6 +863,9 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres if (tos->custom_bevel_profile_preset) { BKE_curveprofile_blend_write(writer, tos->custom_bevel_profile_preset); } + if (tos->sequencer_tool_settings) { + BLO_write_struct(writer, SequencerToolSettings, tos->sequencer_tool_settings); + } BKE_paint_blend_write(writer, &tos->imapaint.paint); @@ -1119,6 +1125,8 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) if (sce->toolsettings->custom_bevel_profile_preset) { BKE_curveprofile_blend_read(reader, sce->toolsettings->custom_bevel_profile_preset); } + + BLO_read_data_address(reader, &sce->toolsettings->sequencer_tool_settings); } if (sce->ed) { @@ -1790,6 +1798,8 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->gp_sculpt.cur_primitive = BKE_curvemapping_copy(ts->gp_sculpt.cur_primitive); ts->custom_bevel_profile_preset = BKE_curveprofile_copy(ts->custom_bevel_profile_preset); + + ts->sequencer_tool_settings = SEQ_tool_settings_copy(ts->sequencer_tool_settings); return ts; } @@ -1848,6 +1858,10 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) BKE_curveprofile_free(toolsettings->custom_bevel_profile_preset); } + if (toolsettings->sequencer_tool_settings) { + SEQ_tool_settings_free(toolsettings->sequencer_tool_settings); + } + MEM_freeN(toolsettings); } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 355db8f0d60..52c41c9fd05 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -34,6 +34,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_collection_types.h" #include "DNA_defaults.h" #include "DNA_gpencil_types.h" #include "DNA_mask_types.h" @@ -1265,6 +1266,9 @@ static void write_area_regions(BlendWriter *writer, ScrArea *area) if (sfile->params) { BLO_write_struct(writer, FileSelectParams, sfile->params); } + if (sfile->asset_params) { + BLO_write_struct(writer, FileAssetSelectParams, sfile->asset_params); + } } else if (sl->spacetype == SPACE_SEQ) { BLO_write_struct(writer, SpaceSeq, sl); @@ -1662,11 +1666,14 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) * plus, it isn't saved to files yet! */ sfile->folders_prev = sfile->folders_next = NULL; + BLI_listbase_clear(&sfile->folder_histories); sfile->files = NULL; sfile->layout = NULL; sfile->op = NULL; sfile->previews_timer = NULL; + sfile->tags = 0; BLO_read_data_address(reader, &sfile->params); + BLO_read_data_address(reader, &sfile->asset_params); } else if (sl->spacetype == SPACE_CLIP) { SpaceClip *sclip = (SpaceClip *)sl; @@ -1750,8 +1757,11 @@ void BKE_screen_area_blend_read_lib(BlendLibReader *reader, ID *parent_id, ScrAr } break; } - case SPACE_FILE: + case SPACE_FILE: { + SpaceFile *sfile = (SpaceFile *)sl; + sfile->tags |= FILE_TAG_REBUILD_MAIN_FILES; break; + } case SPACE_ACTION: { SpaceAction *saction = (SpaceAction *)sl; bDopeSheet *ads = &saction->ads; diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 25d812884bc..736acd76dfd 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -52,6 +52,7 @@ #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_force_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 8b66b1fc628..5bcc1a9553a 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -135,13 +135,19 @@ static void sound_foreach_cache(ID *id, static void sound_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bSound *sound = (bSound *)id; - if (sound->id.us > 0 || BLO_write_is_undo(writer)) { + const bool is_undo = BLO_write_is_undo(writer); + if (sound->id.us > 0 || is_undo) { /* Clean up, important in undo case to reduce false detection of changed datablocks. */ sound->tags = 0; sound->handle = NULL; sound->playback_handle = NULL; sound->spinlock = NULL; + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(sound) && !is_undo) { + sound->packedfile = NULL; + } + /* write LibData */ BLO_write_id_struct(writer, bSound, id_address, &sound->id); BKE_id_blend_write(writer, &sound->id); diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 93795af7cd7..9ef2e818293 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -459,12 +459,14 @@ bool BKE_text_reload(Text *text) return true; } -/** Load a text file. +/** + * Load a text file. * - * \param is_internal If \a true, this text data-block only exists in memory, not as a file on - * disk. + * \param is_internal: If \a true, this text data-block only exists in memory, + * not as a file on disk. * - * \note: text data-blocks have no user by default, only the 'real user' flag. */ + * \note text data-blocks have no user by default, only the 'real user' flag. + */ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const bool is_internal) { unsigned char *buffer; @@ -520,7 +522,8 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const /** Load a text file. * - * \note: text data-blocks have no user by default, only the 'real user' flag. */ + * \note Text data-blocks have no user by default, only the 'real user' flag. + */ Text *BKE_text_load(Main *bmain, const char *file, const char *relpath) { return BKE_text_load_ex(bmain, file, relpath, false); diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 11aa9597740..67727d87161 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -561,10 +561,16 @@ static void volume_foreach_cache(ID *id, static void volume_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Volume *volume = (Volume *)id; - if (volume->id.us > 0 || BLO_write_is_undo(writer)) { + const bool is_undo = BLO_write_is_undo(writer); + if (volume->id.us > 0 || is_undo) { /* Clean up, important in undo case to reduce false detection of changed datablocks. */ volume->runtime.grids = nullptr; + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(volume) && !is_undo) { + volume->packedfile = nullptr; + } + /* write LibData */ BLO_write_id_struct(writer, Volume, id_address, &volume->id); BKE_id_blend_write(writer, &volume->id); diff --git a/source/blender/blenkernel/particle_private.h b/source/blender/blenkernel/particle_private.h index 33277d1caac..1c183aab3b7 100644 --- a/source/blender/blenkernel/particle_private.h +++ b/source/blender/blenkernel/particle_private.h @@ -27,6 +27,8 @@ extern "C" { #endif +struct CurveMapping; + typedef struct ParticleChildModifierContext { ParticleThreadContext *thread_ctx; ParticleSimulationData *sim; @@ -62,7 +64,7 @@ float do_clump(ParticleKey *state, float pa_clump, bool use_clump_noise, float clump_noise_size, - CurveMapping *clumpcurve); + struct CurveMapping *clumpcurve); void do_child_modifiers(const ParticleChildModifierContext *modifier_ctx, float mat[4][4], ParticleKey *state, diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index 8a7dcb7ffaa..284d62fb876 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -220,13 +220,13 @@ class Array { return MutableSpan<T>(data_, size_); } - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr> operator Span<U>() const { return Span<U>(data_, size_); } - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr> operator MutableSpan<U>() { return MutableSpan<U>(data_, size_); diff --git a/source/blender/blenlib/BLI_hash.h b/source/blender/blenlib/BLI_hash.h index c2be416ef5f..d687e805323 100644 --- a/source/blender/blenlib/BLI_hash.h +++ b/source/blender/blenlib/BLI_hash.h @@ -26,35 +26,59 @@ extern "C" { #endif -BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky) -{ +/** + * Jenkins Lookup3 Hash Functions. + * Source: http://burtleburtle.net/bob/c/lookup3.c + */ + #define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) +#define final(a, b, c) \ + { \ + c ^= b; \ + c -= rot(b, 14); \ + a ^= c; \ + a -= rot(c, 11); \ + b ^= a; \ + b -= rot(a, 25); \ + c ^= b; \ + c -= rot(b, 16); \ + a ^= c; \ + a -= rot(c, 4); \ + b ^= a; \ + b -= rot(a, 14); \ + c ^= b; \ + c -= rot(b, 24); \ + } \ + ((void)0) + +BLI_INLINE unsigned int BLI_hash_int_3d(unsigned int kx, unsigned int ky, unsigned int kz) +{ + unsigned int a, b, c; + a = b = c = 0xdeadbeef + (3 << 2) + 13; + + c += kz; + b += ky; + a += kx; + final(a, b, c); + + return c; +} +BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky) +{ unsigned int a, b, c; a = b = c = 0xdeadbeef + (2 << 2) + 13; a += kx; b += ky; - c ^= b; - c -= rot(b, 14); - a ^= c; - a -= rot(c, 11); - b ^= a; - b -= rot(a, 25); - c ^= b; - c -= rot(b, 16); - a ^= c; - a -= rot(c, 4); - b ^= a; - b -= rot(a, 14); - c ^= b; - c -= rot(b, 24); + final(a, b, c); return c; +} +#undef final #undef rot -} BLI_INLINE unsigned int BLI_hash_string(const char *str) { diff --git a/source/blender/blenlib/BLI_index_range.hh b/source/blender/blenlib/BLI_index_range.hh index 2b060c986cd..4121542c887 100644 --- a/source/blender/blenlib/BLI_index_range.hh +++ b/source/blender/blenlib/BLI_index_range.hh @@ -73,21 +73,22 @@ class IndexRange { int64_t size_ = 0; public: - IndexRange() = default; + constexpr IndexRange() = default; - explicit IndexRange(int64_t size) : start_(0), size_(size) + constexpr explicit IndexRange(int64_t size) : start_(0), size_(size) { BLI_assert(size >= 0); } - IndexRange(int64_t start, int64_t size) : start_(start), size_(size) + constexpr IndexRange(int64_t start, int64_t size) : start_(start), size_(size) { BLI_assert(start >= 0); BLI_assert(size >= 0); } template<typename T> - IndexRange(const tbb::blocked_range<T> &range) : start_(range.begin()), size_(range.size()) + constexpr IndexRange(const tbb::blocked_range<T> &range) + : start_(range.begin()), size_(range.size()) { } @@ -96,33 +97,33 @@ class IndexRange { int64_t current_; public: - Iterator(int64_t current) : current_(current) + constexpr Iterator(int64_t current) : current_(current) { } - Iterator &operator++() + constexpr Iterator &operator++() { current_++; return *this; } - bool operator!=(const Iterator &iterator) const + constexpr bool operator!=(const Iterator &iterator) const { return current_ != iterator.current_; } - int64_t operator*() const + constexpr int64_t operator*() const { return current_; } }; - Iterator begin() const + constexpr Iterator begin() const { return Iterator(start_); } - Iterator end() const + constexpr Iterator end() const { return Iterator(start_ + size_); } @@ -130,7 +131,7 @@ class IndexRange { /** * Access an element in the range. */ - int64_t operator[](int64_t index) const + constexpr int64_t operator[](int64_t index) const { BLI_assert(index >= 0); BLI_assert(index < this->size()); @@ -140,7 +141,7 @@ class IndexRange { /** * Two ranges compare equal when they contain the same numbers. */ - friend bool operator==(IndexRange a, IndexRange b) + constexpr friend bool operator==(IndexRange a, IndexRange b) { return (a.size_ == b.size_) && (a.start_ == b.start_ || a.size_ == 0); } @@ -148,7 +149,7 @@ class IndexRange { /** * Get the amount of numbers in the range. */ - int64_t size() const + constexpr int64_t size() const { return size_; } @@ -156,7 +157,7 @@ class IndexRange { /** * Create a new range starting at the end of the current one. */ - IndexRange after(int64_t n) const + constexpr IndexRange after(int64_t n) const { BLI_assert(n >= 0); return IndexRange(start_ + size_, n); @@ -165,7 +166,7 @@ class IndexRange { /** * Create a new range that ends at the start of the current one. */ - IndexRange before(int64_t n) const + constexpr IndexRange before(int64_t n) const { BLI_assert(n >= 0); return IndexRange(start_ - n, n); @@ -175,7 +176,7 @@ class IndexRange { * Get the first element in the range. * Asserts when the range is empty. */ - int64_t first() const + constexpr int64_t first() const { BLI_assert(this->size() > 0); return start_; @@ -185,7 +186,7 @@ class IndexRange { * Get the last element in the range. * Asserts when the range is empty. */ - int64_t last() const + constexpr int64_t last() const { BLI_assert(this->size() > 0); return start_ + size_ - 1; @@ -194,7 +195,7 @@ class IndexRange { /** * Get the element one after the end. The returned value is undefined when the range is empty. */ - int64_t one_after_last() const + constexpr int64_t one_after_last() const { return start_ + size_; } @@ -202,7 +203,7 @@ class IndexRange { /** * Get the first element in the range. The returned value is undefined when the range is empty. */ - int64_t start() const + constexpr int64_t start() const { return start_; } @@ -210,7 +211,7 @@ class IndexRange { /** * Returns true when the range contains a certain number, otherwise false. */ - bool contains(int64_t value) const + constexpr bool contains(int64_t value) const { return value >= start_ && value < start_ + size_; } @@ -218,7 +219,7 @@ class IndexRange { /** * Returns a new range, that contains a sub-interval of the current one. */ - IndexRange slice(int64_t start, int64_t size) const + constexpr IndexRange slice(int64_t start, int64_t size) const { BLI_assert(start >= 0); BLI_assert(size >= 0); @@ -226,7 +227,7 @@ class IndexRange { BLI_assert(new_start + size <= start_ + size_ || size == 0); return IndexRange(new_start, size); } - IndexRange slice(IndexRange range) const + constexpr IndexRange slice(IndexRange range) const { return this->slice(range.start(), range.size()); } diff --git a/source/blender/blenlib/BLI_inplace_priority_queue.hh b/source/blender/blenlib/BLI_inplace_priority_queue.hh new file mode 100644 index 00000000000..e76cb8504a3 --- /dev/null +++ b/source/blender/blenlib/BLI_inplace_priority_queue.hh @@ -0,0 +1,304 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "BLI_array.hh" +#include "BLI_dot_export.hh" + +namespace blender { + +/** + * An InplacePriorityQueue adds priority queue functionality to an existing array. The underlying + * array is not changed. Instead, the priority queue maintains indices into the original array. + * + * The priority queue provides efficient access to the element in order of their priorities. + * + * When a priority changes, the priority queue has to be informed using one of the following + * methods: #priority_decreased, #priority_increased or #priority_changed. + */ +template< + /* Type of the elements in the underlying array. */ + typename T, + /* Binary function that takes two `const T &` inputs and returns true, when the first input has + greater priority than the second. */ + typename FirstHasHigherPriority = std::greater<T>> +class InplacePriorityQueue { + private: + /* Underlying array the priority queue is built upon. This is a span instead of a mutable span, + * because this data structure never changes the values itself. */ + Span<T> data_; + /* Maps indices from the heap (binary tree in array format) to indices of the underlying/original + * array. */ + Array<int64_t> heap_to_orig_; + /* This is the inversion of the above mapping. */ + Array<int64_t> orig_to_heap_; + /* Number of elements that are currently in the priority queue. */ + int64_t heap_size_ = 0; + /* Function that can be changed to customize how the priority of two elements is compared. */ + FirstHasHigherPriority first_has_higher_priority_fn_; + + public: + /** + * Construct the priority queue on top of the data in the given span. + */ + InplacePriorityQueue(Span<T> data) + : data_(data), heap_to_orig_(data_.size()), orig_to_heap_(data_.size()) + { + for (const int64_t i : IndexRange(data_.size())) { + heap_to_orig_[i] = i; + orig_to_heap_[i] = i; + } + + this->rebuild(); + } + + /** + * Rebuilds the priority queue from the array that has been passed to the constructor. + */ + void rebuild() + { + const int final_heap_size = data_.size(); + if (final_heap_size > 1) { + for (int64_t i = this->get_parent(final_heap_size - 1); i >= 0; i--) { + this->heapify(i, final_heap_size); + } + } + heap_size_ = final_heap_size; + } + + /** + * Returns the number of elements in the priority queue. + * This is less or equal than the size of the underlying array. + */ + int64_t size() const + { + return heap_size_; + } + + /** + * Returns true, when the priority queue contains no elements. If this returns true, #peek and + * #pop must not be used. + */ + bool is_empty() const + { + return heap_size_ == 0; + } + + /** + * Get the element with the highest priority in the priority queue. + * The returned reference is const, because the priority queue has read-only access to the + * underlying data. If you need a mutable reference, use #peek_index instead. + */ + const T &peek() const + { + return data_[this->peek_index()]; + } + + /** + * Get the element with the highest priority in the priority queue and remove it. + * The returned reference is const, because the priority queue has read-only access to the + * underlying data. If you need a mutable reference, use #pop_index instead. + */ + const T &pop() + { + return data_[this->pop_index()]; + } + + /** + * Get the index of the element with the highest priority in the priority queue. + */ + int64_t peek_index() const + { + BLI_assert(!this->is_empty()); + return heap_to_orig_[0]; + } + + /** + * Get the index of the element with the highest priority in the priority queue and remove it. + */ + int64_t pop_index() + { + BLI_assert(!this->is_empty()); + const int64_t top_index_orig = heap_to_orig_[0]; + heap_size_--; + if (heap_size_ > 1) { + this->swap_indices(0, heap_size_); + this->heapify(0, heap_size_); + } + return top_index_orig; + } + + /** + * Inform the priority queue that the priority of the element at the given index has been + * decreased. + */ + void priority_decreased(const int64_t index) + { + const int64_t heap_index = orig_to_heap_[index]; + if (heap_index >= heap_size_) { + /* This element is not in the queue currently. */ + return; + } + this->heapify(heap_index, heap_size_); + } + + /** + * Inform the priority queue that the priority of the element at the given index has been + * increased. + */ + void priority_increased(const int64_t index) + { + int64_t current = orig_to_heap_[index]; + if (current >= heap_size_) { + /* This element is not in the queue currently. */ + return; + } + while (true) { + if (current == 0) { + break; + } + const int64_t parent = this->get_parent(current); + if (this->first_has_higher_priority(parent, current)) { + break; + } + this->swap_indices(current, parent); + current = parent; + } + } + + /** + * Inform the priority queue that the priority of the element at the given index has been + * changed. + */ + void priority_changed(const int64_t index) + { + this->priority_increased(index); + this->priority_decreased(index); + } + + /** + * Returns the indices of all elements that are in the priority queue. + * There are no guarantees about the order of indices. + */ + Span<int64_t> active_indices() const + { + return heap_to_orig_.as_span().take_front(heap_size_); + } + + /** + * Returns the indices of all elements that are not in the priority queue. + * The indices are in reverse order of their removal from the queue. + * I.e. the index that has been removed last, comes first. + */ + Span<int64_t> inactive_indices() const + { + return heap_to_orig_.as_span().drop_front(heap_size_); + } + + /** + * Returns the concatenation of the active and inactive indices. + */ + Span<int64_t> all_indices() const + { + return heap_to_orig_; + } + + /** + * Return the heap used by the priority queue as dot graph string. + * This exists for debugging purposes. + */ + std::string to_dot() const + { + return this->partial_to_dot(heap_size_); + } + + private: + bool first_has_higher_priority(const int64_t a, const int64_t b) + { + const T &value_a = data_[heap_to_orig_[a]]; + const T &value_b = data_[heap_to_orig_[b]]; + return first_has_higher_priority_fn_(value_a, value_b); + } + + void swap_indices(const int64_t a, const int64_t b) + { + std::swap(heap_to_orig_[a], heap_to_orig_[b]); + orig_to_heap_[heap_to_orig_[a]] = a; + orig_to_heap_[heap_to_orig_[b]] = b; + } + + void heapify(const int64_t parent, const int64_t heap_size) + { + int64_t max_index = parent; + const int left = this->get_left(parent); + const int right = this->get_right(parent); + if (left < heap_size && this->first_has_higher_priority(left, max_index)) { + max_index = left; + } + if (right < heap_size && this->first_has_higher_priority(right, max_index)) { + max_index = right; + } + if (max_index != parent) { + this->swap_indices(parent, max_index); + this->heapify(max_index, heap_size); + } + if (left < heap_size) { + BLI_assert(!this->first_has_higher_priority(left, parent)); + } + if (right < heap_size) { + BLI_assert(!this->first_has_higher_priority(right, parent)); + } + } + + int64_t get_parent(const int64_t child) const + { + BLI_assert(child > 0); + return (child - 1) / 2; + } + + int64_t get_left(const int64_t parent) const + { + return parent * 2 + 1; + } + + int64_t get_right(const int64_t parent) const + { + return parent * 2 + 2; + } + + std::string partial_to_dot(const int size) const + { + dot::DirectedGraph digraph; + Array<dot::Node *> dot_nodes(size); + for (const int i : IndexRange(size)) { + std::stringstream ss; + ss << data_[heap_to_orig_[i]]; + const std::string name = ss.str(); + dot::Node &node = digraph.new_node(name); + node.set_shape(dot::Attr_shape::Rectangle); + node.attributes.set("ordering", "out"); + dot_nodes[i] = &node; + if (i > 0) { + const int64_t parent = this->get_parent(i); + digraph.new_edge(*dot_nodes[parent], node); + } + } + return digraph.to_dot_string(); + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h index c34b71a60f9..5e0ea4f2a99 100644 --- a/source/blender/blenlib/BLI_kdopbvh.h +++ b/source/blender/blenlib/BLI_kdopbvh.h @@ -178,6 +178,7 @@ int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersec int BLI_bvhtree_get_len(const BVHTree *tree); int BLI_bvhtree_get_tree_type(const BVHTree *tree); float BLI_bvhtree_get_epsilon(const BVHTree *tree); +void BLI_bvhtree_get_bounding_box(BVHTree *tree, float r_bb_min[3], float r_bb_max[3]); /* find nearest node to the given coordinates * (if nearest is given it will only search nodes where diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index 49076bb1aae..b3b6855089e 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -428,6 +428,25 @@ inline constexpr bool is_convertible_pointer_v = std::is_convertible_v<From, To> &&std::is_pointer_v<From> &&std::is_pointer_v<To>; /** + * Helper variable that checks if a Span<From> can be converted to Span<To> safely, whereby From + * and To are pointers. Adding const and casting to a void pointer is allowed. + * Casting up and down a class hierarchy generally is not allowed, because this might change the + * pointer under some circumstances. + */ +template<typename From, typename To> +inline constexpr bool is_span_convertible_pointer_v = + /* Make sure we are working with pointers. */ + std::is_pointer_v<From> &&std::is_pointer_v<To> && + (/* No casting is necessary when both types are the same. */ + std::is_same_v<From, To> || + /* Allow adding const to the underlying type. */ + std::is_same_v<const std::remove_pointer_t<From>, std::remove_pointer_t<To>> || + /* Allow casting non-const pointers to void pointers. */ + (!std::is_const_v<std::remove_pointer_t<From>> && std::is_same_v<To, void *>) || + /* Allow casting any pointer to const void pointers. */ + std::is_same_v<To, const void *>); + +/** * Inline buffers for small-object-optimization should be disable by default. Otherwise we might * get large unexpected allocations on the stack. */ diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index 5b4d2769f57..8011b2f9abc 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -93,15 +93,15 @@ template<typename T> class Span { /** * Create a reference to an empty array. */ - Span() = default; + constexpr Span() = default; - Span(const T *start, int64_t size) : data_(start), size_(size) + constexpr Span(const T *start, int64_t size) : data_(start), size_(size) { BLI_assert(size >= 0); } - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> - Span(const U *start, int64_t size) : data_(static_cast<const T *>(start)), size_(size) + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<U, T>> * = nullptr> + constexpr Span(const U *start, int64_t size) : data_(static_cast<const T *>(start)), size_(size) { BLI_assert(size >= 0); } @@ -117,16 +117,17 @@ template<typename T> class Span { * Span<int> span = {1, 2, 3, 4}; * call_function_with_array(span); */ - Span(const std::initializer_list<T> &list) + constexpr Span(const std::initializer_list<T> &list) : Span(list.begin(), static_cast<int64_t>(list.size())) { } - Span(const std::vector<T> &vector) : Span(vector.data(), static_cast<int64_t>(vector.size())) + constexpr Span(const std::vector<T> &vector) + : Span(vector.data(), static_cast<int64_t>(vector.size())) { } - template<std::size_t N> Span(const std::array<T, N> &array) : Span(array.data(), N) + template<std::size_t N> constexpr Span(const std::array<T, N> &array) : Span(array.data(), N) { } @@ -134,8 +135,9 @@ template<typename T> class Span { * Support implicit conversions like the ones below: * Span<T *> -> Span<const T *> */ - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> - Span(Span<U> array) : data_(static_cast<const T *>(array.data())), size_(array.size()) + + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<U, T>> * = nullptr> + constexpr Span(Span<U> array) : data_(static_cast<const T *>(array.data())), size_(array.size()) { } @@ -143,7 +145,7 @@ template<typename T> class Span { * Returns a contiguous part of the array. This invokes undefined behavior when the slice does * not stay within the bounds of the array. */ - Span slice(int64_t start, int64_t size) const + constexpr Span slice(int64_t start, int64_t size) const { BLI_assert(start >= 0); BLI_assert(size >= 0); @@ -151,7 +153,7 @@ template<typename T> class Span { return Span(data_ + start, size); } - Span slice(IndexRange range) const + constexpr Span slice(IndexRange range) const { return this->slice(range.start(), range.size()); } @@ -160,7 +162,7 @@ template<typename T> class Span { * Returns a new Span with n elements removed from the beginning. This invokes undefined * behavior when the array is too small. */ - Span drop_front(int64_t n) const + constexpr Span drop_front(int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= this->size()); @@ -171,7 +173,7 @@ template<typename T> class Span { * Returns a new Span with n elements removed from the beginning. This invokes undefined * behavior when the array is too small. */ - Span drop_back(int64_t n) const + constexpr Span drop_back(int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= this->size()); @@ -182,7 +184,7 @@ template<typename T> class Span { * Returns a new Span that only contains the first n elements. This invokes undefined * behavior when the array is too small. */ - Span take_front(int64_t n) const + constexpr Span take_front(int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= this->size()); @@ -193,7 +195,7 @@ template<typename T> class Span { * Returns a new Span that only contains the last n elements. This invokes undefined * behavior when the array is too small. */ - Span take_back(int64_t n) const + constexpr Span take_back(int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= this->size()); @@ -204,25 +206,25 @@ template<typename T> class Span { * Returns the pointer to the beginning of the referenced array. This may be nullptr when the * size is zero. */ - const T *data() const + constexpr const T *data() const { return data_; } - const T *begin() const + constexpr const T *begin() const { return data_; } - const T *end() const + constexpr const T *end() const { return data_ + size_; } - std::reverse_iterator<const T *> rbegin() const + constexpr std::reverse_iterator<const T *> rbegin() const { return std::reverse_iterator<const T *>(this->end()); } - std::reverse_iterator<const T *> rend() const + constexpr std::reverse_iterator<const T *> rend() const { return std::reverse_iterator<const T *>(this->begin()); } @@ -231,7 +233,7 @@ template<typename T> class Span { * Access an element in the array. This invokes undefined behavior when the index is out of * bounds. */ - const T &operator[](int64_t index) const + constexpr const T &operator[](int64_t index) const { BLI_assert(index >= 0); BLI_assert(index < size_); @@ -241,7 +243,7 @@ template<typename T> class Span { /** * Returns the number of elements in the referenced array. */ - int64_t size() const + constexpr int64_t size() const { return size_; } @@ -249,7 +251,7 @@ template<typename T> class Span { /** * Returns true if the size is zero. */ - bool is_empty() const + constexpr bool is_empty() const { return size_ == 0; } @@ -257,7 +259,7 @@ template<typename T> class Span { /** * Returns the number of bytes referenced by this Span. */ - int64_t size_in_bytes() const + constexpr int64_t size_in_bytes() const { return sizeof(T) * size_; } @@ -266,7 +268,7 @@ template<typename T> class Span { * Does a linear search to see of the value is in the array. * Returns true if it is, otherwise false. */ - bool contains(const T &value) const + constexpr bool contains(const T &value) const { for (const T &element : *this) { if (element == value) { @@ -280,7 +282,7 @@ template<typename T> class Span { * Does a constant time check to see if the pointer points to a value in the referenced array. * Return true if it is, otherwise false. */ - bool contains_ptr(const T *ptr) const + constexpr bool contains_ptr(const T *ptr) const { return (this->begin() <= ptr) && (ptr < this->end()); } @@ -289,7 +291,7 @@ template<typename T> class Span { * Does a linear search to count how often the value is in the array. * Returns the number of occurrences. */ - int64_t count(const T &value) const + constexpr int64_t count(const T &value) const { int64_t counter = 0; for (const T &element : *this) { @@ -304,7 +306,7 @@ template<typename T> class Span { * Return a reference to the first element in the array. This invokes undefined behavior when the * array is empty. */ - const T &first() const + constexpr const T &first() const { BLI_assert(size_ > 0); return data_[0]; @@ -314,7 +316,7 @@ template<typename T> class Span { * Returns a reference to the last element in the array. This invokes undefined behavior when the * array is empty. */ - const T &last() const + constexpr const T &last() const { BLI_assert(size_ > 0); return data_[size_ - 1]; @@ -324,7 +326,7 @@ template<typename T> class Span { * Returns the element at the given index. If the index is out of range, return the fallback * value. */ - T get(int64_t index, const T &fallback) const + constexpr T get(int64_t index, const T &fallback) const { if (index < size_ && index >= 0) { return data_[index]; @@ -336,7 +338,7 @@ template<typename T> class Span { * Check if the array contains duplicates. Does a linear search for every element. So the total * running time is O(n^2). Only use this for small arrays. */ - bool has_duplicates__linear_search() const + constexpr bool has_duplicates__linear_search() const { /* The size should really be smaller than that. If it is not, the calling code should be * changed. */ @@ -358,7 +360,7 @@ template<typename T> class Span { * called on small arrays, because it has a running time of O(n*m) where n and m are the sizes of * the arrays. */ - bool intersects__linear_search(Span other) const + constexpr bool intersects__linear_search(Span other) const { /* The size should really be smaller than that. If it is not, the calling code should be * changed. */ @@ -377,7 +379,7 @@ template<typename T> class Span { * Returns the index of the first occurrence of the given value. This invokes undefined behavior * when the value is not in the array. */ - int64_t first_index(const T &search_value) const + constexpr int64_t first_index(const T &search_value) const { const int64_t index = this->first_index_try(search_value); BLI_assert(index >= 0); @@ -387,7 +389,7 @@ template<typename T> class Span { /** * Returns the index of the first occurrence of the given value or -1 if it does not exist. */ - int64_t first_index_try(const T &search_value) const + constexpr int64_t first_index_try(const T &search_value) const { for (int64_t i = 0; i < size_; i++) { if (data_[i] == search_value) { @@ -401,7 +403,7 @@ template<typename T> class Span { * Utility to make it more convenient to iterate over all indices that can be used with this * array. */ - IndexRange index_range() const + constexpr IndexRange index_range() const { return IndexRange(size_); } @@ -409,7 +411,7 @@ template<typename T> class Span { /** * Returns a new Span to the same underlying memory buffer. No conversions are done. */ - template<typename NewT> Span<NewT> cast() const + template<typename NewT> Span<NewT> constexpr cast() const { BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0); int64_t new_size = size_ * sizeof(T) / sizeof(NewT); @@ -450,21 +452,22 @@ template<typename T> class MutableSpan { int64_t size_; public: - MutableSpan() = default; + constexpr MutableSpan() = default; - MutableSpan(T *start, const int64_t size) : data_(start), size_(size) + constexpr MutableSpan(T *start, const int64_t size) : data_(start), size_(size) { } - MutableSpan(std::vector<T> &vector) : MutableSpan(vector.data(), vector.size()) + constexpr MutableSpan(std::vector<T> &vector) : MutableSpan(vector.data(), vector.size()) { } - template<std::size_t N> MutableSpan(std::array<T, N> &array) : MutableSpan(array.data(), N) + template<std::size_t N> + constexpr MutableSpan(std::array<T, N> &array) : MutableSpan(array.data(), N) { } - operator Span<T>() const + constexpr operator Span<T>() const { return Span<T>(data_, size_); } @@ -472,7 +475,7 @@ template<typename T> class MutableSpan { /** * Returns the number of elements in the array. */ - int64_t size() const + constexpr int64_t size() const { return size_; } @@ -480,7 +483,7 @@ template<typename T> class MutableSpan { /** * Replace all elements in the referenced array with the given value. */ - void fill(const T &value) + constexpr void fill(const T &value) { initialized_fill_n(data_, size_, value); } @@ -489,7 +492,7 @@ template<typename T> class MutableSpan { * Replace a subset of all elements with the given value. This invokes undefined behavior when * one of the indices is out of bounds. */ - void fill_indices(Span<int64_t> indices, const T &value) + constexpr void fill_indices(Span<int64_t> indices, const T &value) { for (int64_t i : indices) { BLI_assert(i < size_); @@ -501,30 +504,30 @@ template<typename T> class MutableSpan { * Returns a pointer to the beginning of the referenced array. This may be nullptr, when the size * is zero. */ - T *data() const + constexpr T *data() const { return data_; } - T *begin() const + constexpr T *begin() const { return data_; } - T *end() const + constexpr T *end() const { return data_ + size_; } - std::reverse_iterator<T *> rbegin() const + constexpr std::reverse_iterator<T *> rbegin() const { return std::reverse_iterator<T *>(this->end()); } - std::reverse_iterator<T *> rend() const + constexpr std::reverse_iterator<T *> rend() const { return std::reverse_iterator<T *>(this->begin()); } - T &operator[](const int64_t index) const + constexpr T &operator[](const int64_t index) const { BLI_assert(index < this->size()); return data_[index]; @@ -534,7 +537,7 @@ template<typename T> class MutableSpan { * Returns a contiguous part of the array. This invokes undefined behavior when the slice would * go out of bounds. */ - MutableSpan slice(const int64_t start, const int64_t length) const + constexpr MutableSpan slice(const int64_t start, const int64_t length) const { BLI_assert(start + length <= this->size()); return MutableSpan(data_ + start, length); @@ -544,7 +547,7 @@ template<typename T> class MutableSpan { * Returns a new MutableSpan with n elements removed from the beginning. This invokes * undefined behavior when the array is too small. */ - MutableSpan drop_front(const int64_t n) const + constexpr MutableSpan drop_front(const int64_t n) const { BLI_assert(n <= this->size()); return this->slice(n, this->size() - n); @@ -554,7 +557,7 @@ template<typename T> class MutableSpan { * Returns a new MutableSpan with n elements removed from the end. This invokes undefined * behavior when the array is too small. */ - MutableSpan drop_back(const int64_t n) const + constexpr MutableSpan drop_back(const int64_t n) const { BLI_assert(n <= this->size()); return this->slice(0, this->size() - n); @@ -564,7 +567,7 @@ template<typename T> class MutableSpan { * Returns a new MutableSpan that only contains the first n elements. This invokes undefined * behavior when the array is too small. */ - MutableSpan take_front(const int64_t n) const + constexpr MutableSpan take_front(const int64_t n) const { BLI_assert(n <= this->size()); return this->slice(0, n); @@ -574,7 +577,7 @@ template<typename T> class MutableSpan { * Return a new MutableSpan that only contains the last n elements. This invokes undefined * behavior when the array is too small. */ - MutableSpan take_back(const int64_t n) const + constexpr MutableSpan take_back(const int64_t n) const { BLI_assert(n <= this->size()); return this->slice(this->size() - n, n); @@ -584,7 +587,7 @@ template<typename T> class MutableSpan { * Returns an (immutable) Span that references the same array. This is usually not needed, * due to implicit conversions. However, sometimes automatic type deduction needs some help. */ - Span<T> as_span() const + constexpr Span<T> as_span() const { return Span<T>(data_, size_); } @@ -593,7 +596,7 @@ template<typename T> class MutableSpan { * Utility to make it more convenient to iterate over all indices that can be used with this * array. */ - IndexRange index_range() const + constexpr IndexRange index_range() const { return IndexRange(size_); } @@ -602,7 +605,7 @@ template<typename T> class MutableSpan { * Returns a reference to the last element. This invokes undefined behavior when the array is * empty. */ - T &last() const + constexpr T &last() const { BLI_assert(size_ > 0); return data_[size_ - 1]; @@ -612,7 +615,7 @@ template<typename T> class MutableSpan { * Does a linear search to count how often the value is in the array. * Returns the number of occurrences. */ - int64_t count(const T &value) const + constexpr int64_t count(const T &value) const { int64_t counter = 0; for (const T &element : *this) { @@ -628,7 +631,7 @@ template<typename T> class MutableSpan { * destination contains uninitialized data and T is not trivially copy constructible. * The size of both spans is expected to be the same. */ - void copy_from(Span<T> values) + constexpr void copy_from(Span<T> values) { BLI_assert(size_ == values.size()); initialized_copy_n(values.data(), size_, data_); @@ -637,7 +640,7 @@ template<typename T> class MutableSpan { /** * Returns a new span to the same underlying memory buffer. No conversions are done. */ - template<typename NewT> MutableSpan<NewT> cast() const + template<typename NewT> constexpr MutableSpan<NewT> cast() const { BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0); int64_t new_size = size_ * sizeof(T) / sizeof(NewT); @@ -648,7 +651,7 @@ template<typename T> class MutableSpan { /** * Utilities to check that arrays have the same size in debug builds. */ -template<typename T1, typename T2> void assert_same_size(const T1 &v1, const T2 &v2) +template<typename T1, typename T2> constexpr void assert_same_size(const T1 &v1, const T2 &v2) { UNUSED_VARS_NDEBUG(v1, v2); #ifdef DEBUG @@ -659,7 +662,7 @@ template<typename T1, typename T2> void assert_same_size(const T1 &v1, const T2 } template<typename T1, typename T2, typename T3> -void assert_same_size(const T1 &v1, const T2 &v2, const T3 &v3) +constexpr void assert_same_size(const T1 &v1, const T2 &v2, const T3 &v3) { UNUSED_VARS_NDEBUG(v1, v2, v3); #ifdef DEBUG diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index 8597e54d03b..a2562c6100a 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -64,7 +64,7 @@ class StringRefBase { const char *data_; int64_t size_; - StringRefBase(const char *data, const int64_t size) : data_(data), size_(size) + constexpr StringRefBase(const char *data, const int64_t size) : data_(data), size_(size) { } @@ -75,12 +75,12 @@ class StringRefBase { /** * Return the (byte-)length of the referenced string, without any null-terminator. */ - int64_t size() const + constexpr int64_t size() const { return size_; } - bool is_empty() const + constexpr bool is_empty() const { return size_ == 0; } @@ -88,12 +88,12 @@ class StringRefBase { /** * Return a pointer to the start of the string. */ - const char *data() const + constexpr const char *data() const { return data_; } - operator Span<char>() const + constexpr operator Span<char>() const { return Span<char>(data_, size_); } @@ -107,22 +107,22 @@ class StringRefBase { return std::string(data_, static_cast<size_t>(size_)); } - operator std::string_view() const + constexpr operator std::string_view() const { return std::string_view(data_, static_cast<size_t>(size_)); } - const char *begin() const + constexpr const char *begin() const { return data_; } - const char *end() const + constexpr const char *end() const { return data_ + size_; } - IndexRange index_range() const + constexpr IndexRange index_range() const { return IndexRange(size_); } @@ -165,19 +165,19 @@ class StringRefBase { /** * Returns true when the string begins with the given prefix. Otherwise false. */ - bool startswith(StringRef prefix) const; + constexpr bool startswith(StringRef prefix) const; /** * Returns true when the string ends with the given suffix. Otherwise false. */ - bool endswith(StringRef suffix) const; + constexpr bool endswith(StringRef suffix) const; - StringRef substr(int64_t start, const int64_t size) const; + constexpr StringRef substr(int64_t start, const int64_t size) const; /** * Get the first char in the string. This invokes undefined behavior when the string is empty. */ - const char &front() const + constexpr const char &front() const { BLI_assert(size_ >= 1); return data_[0]; @@ -186,7 +186,7 @@ class StringRefBase { /** * Get the last char in the string. This invokes undefined behavior when the string is empty. */ - const char &back() const + constexpr const char &back() const { BLI_assert(size_ >= 1); return data_[size_ - 1]; @@ -196,18 +196,18 @@ class StringRefBase { * The behavior of those functions matches the standard library implementation of * std::string_view. */ - int64_t find(char c, int64_t pos = 0) const; - int64_t find(StringRef str, int64_t pos = 0) const; - int64_t rfind(char c, int64_t pos = INT64_MAX) const; - int64_t rfind(StringRef str, int64_t pos = INT64_MAX) const; - int64_t find_first_of(StringRef chars, int64_t pos = 0) const; - int64_t find_first_of(char c, int64_t pos = 0) const; - int64_t find_last_of(StringRef chars, int64_t pos = INT64_MAX) const; - int64_t find_last_of(char c, int64_t pos = INT64_MAX) const; - int64_t find_first_not_of(StringRef chars, int64_t pos = 0) const; - int64_t find_first_not_of(char c, int64_t pos = 0) const; - int64_t find_last_not_of(StringRef chars, int64_t pos = INT64_MAX) const; - int64_t find_last_not_of(char c, int64_t pos = INT64_MAX) const; + constexpr int64_t find(char c, int64_t pos = 0) const; + constexpr int64_t find(StringRef str, int64_t pos = 0) const; + constexpr int64_t rfind(char c, int64_t pos = INT64_MAX) const; + constexpr int64_t rfind(StringRef str, int64_t pos = INT64_MAX) const; + constexpr int64_t find_first_of(StringRef chars, int64_t pos = 0) const; + constexpr int64_t find_first_of(char c, int64_t pos = 0) const; + constexpr int64_t find_last_of(StringRef chars, int64_t pos = INT64_MAX) const; + constexpr int64_t find_last_of(char c, int64_t pos = INT64_MAX) const; + constexpr int64_t find_first_not_of(StringRef chars, int64_t pos = 0) const; + constexpr int64_t find_first_not_of(char c, int64_t pos = 0) const; + constexpr int64_t find_last_not_of(StringRef chars, int64_t pos = INT64_MAX) const; + constexpr int64_t find_last_not_of(char c, int64_t pos = INT64_MAX) const; }; /** @@ -216,7 +216,7 @@ class StringRefBase { class StringRefNull : public StringRefBase { public: - StringRefNull() : StringRefBase("", 0) + constexpr StringRefNull() : StringRefBase("", 0) { } @@ -226,7 +226,7 @@ class StringRefNull : public StringRefBase { */ StringRefNull(const char *str) : StringRefBase(str, static_cast<int64_t>(strlen(str))) { - BLI_assert(str != NULL); + BLI_assert(str != nullptr); BLI_assert(data_[size_] == '\0'); } @@ -234,7 +234,7 @@ class StringRefNull : public StringRefBase { * Construct a StringRefNull from a null terminated c-string. This invokes undefined behavior * when the given size is not the correct size of the string. */ - StringRefNull(const char *str, const int64_t size) : StringRefBase(str, size) + constexpr StringRefNull(const char *str, const int64_t size) : StringRefBase(str, size) { BLI_assert(static_cast<int64_t>(strlen(str)) == size); } @@ -250,7 +250,7 @@ class StringRefNull : public StringRefBase { /** * Get the char at the given index. */ - char operator[](const int64_t index) const + constexpr char operator[](const int64_t index) const { BLI_assert(index >= 0); /* Use '<=' instead of just '<', so that the null character can be accessed as well. */ @@ -263,7 +263,7 @@ class StringRefNull : public StringRefBase { * * This is like ->data(), but can only be called on a StringRefNull. */ - const char *c_str() const + constexpr const char *c_str() const { return data_; } @@ -274,25 +274,26 @@ class StringRefNull : public StringRefBase { */ class StringRef : public StringRefBase { public: - StringRef() : StringRefBase(nullptr, 0) + constexpr StringRef() : StringRefBase(nullptr, 0) { } /** * StringRefNull can be converted into StringRef, but not the other way around. */ - StringRef(StringRefNull other) : StringRefBase(other.data(), other.size()) + constexpr StringRef(StringRefNull other) : StringRefBase(other.data(), other.size()) { } /** * Create a StringRef from a null-terminated c-string. */ - StringRef(const char *str) : StringRefBase(str, str ? static_cast<int64_t>(strlen(str)) : 0) + constexpr StringRef(const char *str) + : StringRefBase(str, str ? static_cast<int64_t>(std::char_traits<char>::length(str)) : 0) { } - StringRef(const char *str, const int64_t length) : StringRefBase(str, length) + constexpr StringRef(const char *str, const int64_t length) : StringRefBase(str, length) { } @@ -300,7 +301,7 @@ class StringRef : public StringRefBase { * Create a StringRef from a start and end pointer. This invokes undefined behavior when the * second point points to a smaller address than the first one. */ - StringRef(const char *begin, const char *one_after_end) + constexpr StringRef(const char *begin, const char *one_after_end) : StringRefBase(begin, static_cast<int64_t>(one_after_end - begin)) { BLI_assert(begin <= one_after_end); @@ -314,7 +315,8 @@ class StringRef : public StringRefBase { { } - StringRef(std::string_view view) : StringRefBase(view.data(), static_cast<int64_t>(view.size())) + constexpr StringRef(std::string_view view) + : StringRefBase(view.data(), static_cast<int64_t>(view.size())) { } @@ -323,7 +325,7 @@ class StringRef : public StringRefBase { * * This is similar to std::string_view::remove_prefix. */ - StringRef drop_prefix(const int64_t n) const + constexpr StringRef drop_prefix(const int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= size_); @@ -334,7 +336,7 @@ class StringRef : public StringRefBase { * Return a new StringRef with the given prefix being skipped. This invokes undefined behavior if * the string does not begin with the given prefix. */ - StringRef drop_prefix(StringRef prefix) const + constexpr StringRef drop_prefix(StringRef prefix) const { BLI_assert(this->startswith(prefix)); return this->drop_prefix(prefix.size()); @@ -345,7 +347,7 @@ class StringRef : public StringRefBase { * * This is similar to std::string_view::remove_suffix. */ - StringRef drop_suffix(const int64_t n) const + constexpr StringRef drop_suffix(const int64_t n) const { BLI_assert(n >= 0); BLI_assert(n <= size_); @@ -355,7 +357,7 @@ class StringRef : public StringRefBase { /** * Get the char at the given index. */ - char operator[](int64_t index) const + constexpr char operator[](int64_t index) const { BLI_assert(index >= 0); BLI_assert(index < size_); @@ -391,7 +393,7 @@ inline std::string operator+(StringRef a, StringRef b) * not a problem when std::string_view is only used at api boundaries. To compare a StringRef and a * std::string_view, one should convert the std::string_view to StringRef (which is very cheap). * Ideally, we only use StringRef in our code to avoid this problem altogether. */ -inline bool operator==(StringRef a, StringRef b) +constexpr inline bool operator==(StringRef a, StringRef b) { if (a.size() != b.size()) { return false; @@ -399,27 +401,27 @@ inline bool operator==(StringRef a, StringRef b) return STREQLEN(a.data(), b.data(), (size_t)a.size()); } -inline bool operator!=(StringRef a, StringRef b) +constexpr inline bool operator!=(StringRef a, StringRef b) { return !(a == b); } -inline bool operator<(StringRef a, StringRef b) +constexpr inline bool operator<(StringRef a, StringRef b) { return std::string_view(a) < std::string_view(b); } -inline bool operator>(StringRef a, StringRef b) +constexpr inline bool operator>(StringRef a, StringRef b) { return std::string_view(a) > std::string_view(b); } -inline bool operator<=(StringRef a, StringRef b) +constexpr inline bool operator<=(StringRef a, StringRef b) { return std::string_view(a) <= std::string_view(b); } -inline bool operator>=(StringRef a, StringRef b) +constexpr inline bool operator>=(StringRef a, StringRef b) { return std::string_view(a) >= std::string_view(b); } @@ -427,7 +429,7 @@ inline bool operator>=(StringRef a, StringRef b) /** * Return true when the string starts with the given prefix. */ -inline bool StringRefBase::startswith(StringRef prefix) const +constexpr inline bool StringRefBase::startswith(StringRef prefix) const { if (size_ < prefix.size_) { return false; @@ -443,7 +445,7 @@ inline bool StringRefBase::startswith(StringRef prefix) const /** * Return true when the string ends with the given suffix. */ -inline bool StringRefBase::endswith(StringRef suffix) const +constexpr inline bool StringRefBase::endswith(StringRef suffix) const { if (size_ < suffix.size_) { return false; @@ -460,8 +462,8 @@ inline bool StringRefBase::endswith(StringRef suffix) const /** * Return a new #StringRef containing only a sub-string of the original string. */ -inline StringRef StringRefBase::substr(const int64_t start, - const int64_t max_size = INT64_MAX) const +constexpr inline StringRef StringRefBase::substr(const int64_t start, + const int64_t max_size = INT64_MAX) const { BLI_assert(max_size >= 0); BLI_assert(start >= 0); @@ -469,7 +471,7 @@ inline StringRef StringRefBase::substr(const int64_t start, return StringRef(data_ + start, substr_size); } -inline int64_t index_or_npos_to_int64(size_t index) +constexpr inline int64_t index_or_npos_to_int64(size_t index) { /* The compiler will probably optimize this check away. */ if (index == std::string_view::npos) { @@ -478,62 +480,62 @@ inline int64_t index_or_npos_to_int64(size_t index) return static_cast<int64_t>(index); } -inline int64_t StringRefBase::find(char c, int64_t pos) const +constexpr inline int64_t StringRefBase::find(char c, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64(std::string_view(*this).find(c, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find(StringRef str, int64_t pos) const +constexpr inline int64_t StringRefBase::find(StringRef str, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64(std::string_view(*this).find(str, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const +constexpr inline int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64( std::string_view(*this).find_first_of(chars, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find_first_of(char c, int64_t pos) const +constexpr inline int64_t StringRefBase::find_first_of(char c, int64_t pos) const { return this->find_first_of(StringRef(&c, 1), pos); } -inline int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const +constexpr inline int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64( std::string_view(*this).find_last_of(chars, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find_last_of(char c, int64_t pos) const +constexpr inline int64_t StringRefBase::find_last_of(char c, int64_t pos) const { return this->find_last_of(StringRef(&c, 1), pos); } -inline int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const +constexpr inline int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64( std::string_view(*this).find_first_not_of(chars, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const +constexpr inline int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const { return this->find_first_not_of(StringRef(&c, 1), pos); } -inline int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const +constexpr inline int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const { BLI_assert(pos >= 0); return index_or_npos_to_int64( std::string_view(*this).find_last_not_of(chars, static_cast<size_t>(pos))); } -inline int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const +constexpr inline int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const { return this->find_last_not_of(StringRef(&c, 1), pos); } diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index d19b5393aa7..eefde1afefb 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -35,7 +35,6 @@ extern "C" { #define BLENDER_MAX_THREADS 1024 struct ListBase; -struct TaskScheduler; /* Threading API */ diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 053dcb6faea..fe6d54ae9e5 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -315,13 +315,13 @@ class Vector { return MutableSpan<T>(begin_, this->size()); } - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr> operator Span<U>() const { return Span<U>(begin_, this->size()); } - template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + template<typename U, typename std::enable_if_t<is_span_convertible_pointer_v<T, U>> * = nullptr> operator MutableSpan<U>() { return MutableSpan<U>(begin_, this->size()); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 46a3ad87dfe..aa4c4efe7d4 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -203,6 +203,7 @@ set(SRC BLI_heap_simple.h BLI_index_mask.hh BLI_index_range.hh + BLI_inplace_priority_queue.hh BLI_iterator.h BLI_jitter_2d.h BLI_kdopbvh.h @@ -390,6 +391,7 @@ if(WITH_GTESTS) tests/BLI_heap_test.cc tests/BLI_index_mask_test.cc tests/BLI_index_range_test.cc + tests/BLI_inplace_priority_queue_test.cc tests/BLI_kdopbvh_test.cc tests/BLI_linear_allocator_test.cc tests/BLI_linklist_lockfree_test.cc diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index f126c5a977b..0f90ad3a490 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -1076,6 +1076,25 @@ float BLI_bvhtree_get_epsilon(const BVHTree *tree) return tree->epsilon; } +/** + * This function returns the bounding box of the BVH tree. + */ +void BLI_bvhtree_get_bounding_box(BVHTree *tree, float r_bb_min[3], float r_bb_max[3]) +{ + BVHNode *root = tree->nodes[tree->totleaf]; + if (root != NULL) { + const float bb_min[3] = {root->bv[0], root->bv[2], root->bv[4]}; + const float bb_max[3] = {root->bv[1], root->bv[3], root->bv[5]}; + copy_v3_v3(r_bb_min, bb_min); + copy_v3_v3(r_bb_max, bb_max); + } + else { + BLI_assert(false); + zero_v3(r_bb_min); + zero_v3(r_bb_max); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index a0ee16bee76..19828e69638 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -372,6 +372,11 @@ void mat3_normalized_to_quat(float q[4], const float mat[3][3]) q[1] = (mat[2][0] + mat[0][2]) * s; q[2] = (mat[2][1] + mat[1][2]) * s; } + + /* Make sure w is nonnegative for a canonical result. */ + if (q[0] < 0) { + negate_v4(q); + } } normalize_qt(q); @@ -556,10 +561,20 @@ void rotation_between_quats_to_quat(float q[4], const float q1[4], const float q * \param r_twist: if not NULL, receives the twist quaternion. * \returns twist angle. */ -float quat_split_swing_and_twist(const float q[4], int axis, float r_swing[4], float r_twist[4]) +float quat_split_swing_and_twist(const float q_in[4], int axis, float r_swing[4], float r_twist[4]) { BLI_assert(axis >= 0 && axis <= 2); + /* The calculation requires a canonical quaternion. */ + float q[4]; + + if (q_in[0] < 0) { + negate_v4_v4(q, q_in); + } + else { + copy_v4_v4(q, q_in); + } + /* Half-twist angle can be computed directly. */ float t = atan2f(q[axis + 1], q[0]); diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 2db939cd628..88d90a7816f 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -401,11 +401,6 @@ class Cell { void add_patch(int p) { - patches_.add_new(p); - } - - void add_patch_non_duplicates(int p) - { patches_.add(p); } @@ -693,7 +688,7 @@ static void merge_cells(int merge_to, int merge_from, CellsInfo &cinfo, PatchesI merge_to_cell = cinfo.cell(final_merge_to); } for (int cell_p : merge_from_cell.patches()) { - merge_to_cell.add_patch_non_duplicates(cell_p); + merge_to_cell.add_patch(cell_p); Patch &patch = pinfo.patch(cell_p); if (patch.cell_above == merge_from) { patch.cell_above = merge_to; diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 74bbe59bc04..5636ffafb6a 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -32,6 +32,7 @@ #include "BLI_fnmatch.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_utf8.h" #include "BLI_utildefines.h" #ifdef WIN32 @@ -287,7 +288,7 @@ void BLI_path_normalize_dir(const char *relabase, char *dir) * (good practice anyway). * REMOVED based on popular demand (see T45900). * Percent '%' char is a bit same case - not recommended to use it, - * but supported by all decent FS/OS around. + * but supported by all decent file-systems/operating-systems around. * * \note On Windows, it also ensures there is no '.' (dot char) at the end of the file, * this can lead to issues. @@ -512,8 +513,8 @@ void BLI_path_normalize_unc_16(wchar_t *path_16) #endif /** - * Replaces *file with a relative version (prefixed by "//") such that BLI_path_abs, given - * the same *relfile, will convert it back to its original value. + * Replaces `file` with a relative version (prefixed by "//") such that #BLI_path_abs, given + * the same `relfile`, will convert it back to its original value. */ void BLI_path_rel(char *file, const char *relfile) { @@ -663,7 +664,7 @@ void BLI_path_rel(char *file, const char *relfile) * \param maxlen: Maximum length of string * \param suffix: String to append to the original string * \param sep: Optional separator character - * \return true if succeeded + * \return true if succeeded */ bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char *sep) { @@ -702,7 +703,7 @@ bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char /** * Replaces path with the path of its parent directory, returning true if - * it was able to find a parent directory within the pathname. + * it was able to find a parent directory within the path. */ bool BLI_path_parent_dir(char *path) { @@ -721,7 +722,7 @@ bool BLI_path_parent_dir(char *path) } /** - * Strips off nonexistent (or non-accessible) subdirectories from the end of *dir, + * Strips off nonexistent (or non-accessible) sub-directories from the end of `dir`, * leaving the path of the lowest-level directory that does exist and we can read. */ bool BLI_path_parent_dir_until_exists(char *dir) @@ -736,9 +737,9 @@ bool BLI_path_parent_dir_until_exists(char *dir) } /** - * Looks for a sequence of "#" characters in the last slash-separated component of *path, + * Looks for a sequence of "#" characters in the last slash-separated component of `path`, * returning the indexes of the first and one past the last character in the sequence in - * *char_start and *char_end respectively. Returns true if such a sequence was found. + * `char_start` and `char_end` respectively. Returns true if such a sequence was found. */ static bool stringframe_chars(const char *path, int *char_start, int *char_end) { @@ -773,7 +774,7 @@ static bool stringframe_chars(const char *path, int *char_start, int *char_end) } /** - * Ensure *path contains at least one "#" character in its last slash-separated + * Ensure `path` contains at least one "#" character in its last slash-separated * component, appending one digits long if not. */ static void ensure_digits(char *path, int digits) @@ -795,7 +796,7 @@ static void ensure_digits(char *path, int digits) } /** - * Replaces "#" character sequence in last slash-separated component of *path + * Replaces "#" character sequence in last slash-separated component of `path` * with frame as decimal integer, with leading zeroes as necessary, to make digits digits. */ bool BLI_path_frame(char *path, int frame, int digits) @@ -817,7 +818,7 @@ bool BLI_path_frame(char *path, int frame, int digits) } /** - * Replaces "#" character sequence in last slash-separated component of *path + * Replaces "#" character sequence in last slash-separated component of `path` * with sta and end as decimal integers, with leading zeroes as necessary, to make digits * digits each, with a hyphen in-between. */ @@ -962,7 +963,7 @@ bool BLI_path_frame_check_chars(const char *path) /** * Creates a display string from path to be used menus and the user interface. - * Like bpy.path.display_name(). + * Like `bpy.path.display_name()`. */ void BLI_path_to_display_name(char *display_name, int maxlen, const char *name) { @@ -1003,7 +1004,7 @@ void BLI_path_to_display_name(char *display_name, int maxlen, const char *name) } /** - * If path begins with "//", strips that and replaces it with basepath directory. + * If path begins with "//", strips that and replaces it with `basepath` directory. * * \note Also converts drive-letter prefix to something more sensible * if this is a non-drive-letter-based system. @@ -1161,7 +1162,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 - * environment variable (Windows-only) onto *name in turn until such a file is found. + * environment variable (Windows-only) onto `name` in turn until such a file is found. * Returns success/failure. */ bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen) @@ -1268,7 +1269,7 @@ bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *na /** * Sets the specified environment variable to the specified value, - * and clears it if val == NULL. + * and clears it if `val == NULL`. */ void BLI_setenv(const char *env, const char *val) { @@ -1304,30 +1305,44 @@ void BLI_setenv_if_new(const char *env, const char *val) /** * Get an env var, result has to be used immediately. * - * On windows getenv gets its variables from a static copy of the environment variables taken at + * On windows #getenv gets its variables from a static copy of the environment variables taken at * process start-up, causing it to not pick up on environment variables created during runtime. * This function uses an alternative method to get environment variables that does pick up on - * runtime environment variables. + * runtime environment variables. The result will be UTF-8 encoded. */ const char *BLI_getenv(const char *env) { #ifdef _MSC_VER - static char buffer[32767]; /* 32767 is the total size of the environment block on windows*/ - if (GetEnvironmentVariableA(env, buffer, sizeof(buffer))) { - return buffer; - } - else { - return NULL; + const char *result = NULL; + /* 32767 is the maximum size of the environment variable on windows, + * reserve one more character for the zero terminator. */ + static wchar_t buffer[32768]; + wchar_t *env_16 = alloc_utf16_from_8(env, 0); + if (env_16) { + if (GetEnvironmentVariableW(env_16, buffer, ARRAY_SIZE(buffer))) { + char *res_utf8 = alloc_utf_8_from_16(buffer, 0); + /* Make sure the result is valid, and will fit into our temporary storage buffer. */ + if (res_utf8) { + if (strlen(res_utf8) + 1 < sizeof(buffer)) { + /* We are re-using the utf16 buffer here, since allocating a second static buffer to + * contain the UTF-8 version to return would be wasteful. */ + memcpy(buffer, res_utf8, strlen(res_utf8) + 1); + result = (const char *)buffer; + } + free(res_utf8); + } + } } + return result; #else return getenv(env); #endif } /** - * Ensures that the parent directory of *name exists. + * Ensures that the parent directory of `name` exists. * - * \return true on success (i.e. given path now exists on FS), false otherwise. + * \return true on success (i.e. given path now exists on file-system), false otherwise. */ bool BLI_make_existing_file(const char *name) { @@ -1339,13 +1354,13 @@ bool BLI_make_existing_file(const char *name) } /** - * Returns in *string the concatenation of *dir and *file (also with *relabase on the - * front if specified and *dir begins with "//"). Normalizes all occurrences of path - * separators, including ensuring there is exactly one between the copies of *dir and *file, - * and between the copies of *relabase and *dir. + * Returns in `string` the concatenation of `dir` and `file` (also with `relabase` on the + * front if specified and `dir` begins with "//"). Normalizes all occurrences of path + * separators, including ensuring there is exactly one between the copies of `dir` and `file`, + * and between the copies of `relabase` and `dir`. * - * \param relabase: Optional prefix to substitute for "//" on front of *dir - * \param string: Area to return result + * \param relabase: Optional prefix to substitute for "//" on front of `dir`. + * \param string: Area to return result. */ void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file) { @@ -1485,9 +1500,8 @@ bool BLI_path_extension_check_array(const char *str, const char **ext_array) } /** - * Semicolon separated wildcards, eg: - * '*.zip;*.py;*.exe' - * does str match any of the semicolon-separated glob patterns in fnmatch. + * Semicolon separated wildcards, eg: `*.zip;*.py;*.exe` + * does str match any of the semicolon-separated glob patterns in #fnmatch. */ bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch) { @@ -1516,10 +1530,11 @@ bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch) } /** - * Does basic validation of the given glob string, to prevent common issues from string truncation. + * Does basic validation of the given glob string, to prevent common issues from string + * truncation. * * For now, only forbids last group to be a wildcard-only one, if there are more than one group - * (i.e. things like "*.txt;*.cpp;*" are changed to "*.txt;*.cpp;") + * (i.e. things like `*.txt;*.cpp;*` are changed to `*.txt;*.cpp;`) * * \returns true if it had to modify given \a ext_fnmatch pattern. */ @@ -1629,7 +1644,7 @@ bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filenam } /** - * Converts `/foo/bar.txt` to "/foo/" and `bar.txt` + * Converts `/foo/bar.txt` to `/foo/` and `bar.txt` * * - Wont change \a string. * - Wont create any directories. @@ -1662,7 +1677,7 @@ void BLI_split_dirfile( } /** - * Copies the parent directory part of string into *dir, max length dirlen. + * Copies the parent directory part of string into `dir`, max length `dirlen`. */ void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen) { @@ -1670,7 +1685,7 @@ void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen) } /** - * Copies the leaf filename part of string into *file, max length filelen. + * Copies the leaf filename part of string into `file`, max length `filelen`. */ void BLI_split_file_part(const char *string, char *file, const size_t filelen) { @@ -1717,7 +1732,7 @@ void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__re /** * Simple appending of filename to dir, does not check for valid path! - * Puts result into *dst, which may be same area as *dir. + * Puts result into `dst`, which may be same area as `dir`. */ void BLI_join_dirfile(char *__restrict dst, const size_t maxlen, @@ -1761,7 +1776,7 @@ void BLI_join_dirfile(char *__restrict dst, * Join multiple strings into a path, ensuring only a single path separator between each, * and trailing slash is kept. * - * \note If you want a trailing slash, add ``SEP_STR`` as the last path argument, + * \note If you want a trailing slash, add `SEP_STR` as the last path argument, * duplicate slashes will be cleaned up. */ size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path, ...) @@ -1845,7 +1860,7 @@ size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *pat } /** - * like pythons os.path.basename() + * like Python's `os.path.basename()` * * \return The pointer into \a path string immediately after last slash, * or start of \a path if none found. diff --git a/source/blender/blenlib/tests/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc index d472ded0f18..ddaa067f50e 100644 --- a/source/blender/blenlib/tests/BLI_index_range_test.cc +++ b/source/blender/blenlib/tests/BLI_index_range_test.cc @@ -140,4 +140,11 @@ TEST(index_range, AsSpan) EXPECT_EQ(span[3], 7); } +TEST(index_range, constexpr_) +{ + constexpr IndexRange range = IndexRange(1, 1); + std::array<int, range[0]> compiles = {1}; + BLI_STATIC_ASSERT(range.size() == 1, ""); + EXPECT_EQ(compiles[0], 1); +} } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_inplace_priority_queue_test.cc b/source/blender/blenlib/tests/BLI_inplace_priority_queue_test.cc new file mode 100644 index 00000000000..3adf12f36a7 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_inplace_priority_queue_test.cc @@ -0,0 +1,113 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_inplace_priority_queue.hh" +#include "BLI_rand.hh" + +namespace blender::tests { + +TEST(inplace_priority_queue, BuildSmall) +{ + Array<int> values = {1, 5, 2, 8, 5, 6, 5, 4, 3, 6, 7, 3}; + InplacePriorityQueue<int> priority_queue{values}; + + EXPECT_EQ(priority_queue.peek(), 8); + EXPECT_EQ(priority_queue.pop(), 8); + EXPECT_EQ(priority_queue.peek(), 7); + EXPECT_EQ(priority_queue.pop(), 7); + EXPECT_EQ(priority_queue.pop(), 6); + EXPECT_EQ(priority_queue.pop(), 6); + EXPECT_EQ(priority_queue.pop(), 5); +} + +TEST(inplace_priority_queue, DecreasePriority) +{ + Array<int> values = {5, 2, 7, 4}; + InplacePriorityQueue<int> priority_queue(values); + + EXPECT_EQ(priority_queue.peek(), 7); + values[2] = 0; + EXPECT_EQ(priority_queue.peek(), 0); + priority_queue.priority_decreased(2); + EXPECT_EQ(priority_queue.peek(), 5); +} + +TEST(inplace_priority_queue, IncreasePriority) +{ + Array<int> values = {5, 2, 7, 4}; + InplacePriorityQueue<int> priority_queue(values); + + EXPECT_EQ(priority_queue.peek(), 7); + values[1] = 10; + EXPECT_EQ(priority_queue.peek(), 7); + priority_queue.priority_increased(1); + EXPECT_EQ(priority_queue.peek(), 10); +} + +TEST(inplace_priority_queue, PopAll) +{ + RandomNumberGenerator rng; + Vector<int> values; + const int amount = 1000; + for (int i = 0; i < amount; i++) { + values.append(rng.get_int32() % amount); + } + + InplacePriorityQueue<int> priority_queue(values); + + int last_value = amount; + while (!priority_queue.is_empty()) { + const int value = priority_queue.pop(); + EXPECT_LE(value, last_value); + last_value = value; + } +} + +TEST(inplace_priority_queue, ManyPriorityChanges) +{ + RandomNumberGenerator rng; + Vector<int> values; + const int amount = 1000; + for (int i = 0; i < amount; i++) { + values.append(rng.get_int32() % amount); + } + + InplacePriorityQueue<int> priority_queue(values); + + for (int i = 0; i < amount; i++) { + const int index = rng.get_int32() % amount; + const int new_priority = rng.get_int32() % amount; + values[index] = new_priority; + priority_queue.priority_changed(index); + } + + int last_value = amount; + while (!priority_queue.is_empty()) { + const int value = priority_queue.pop(); + EXPECT_LE(value, last_value); + last_value = value; + } +} + +TEST(inplace_priority_queue, IndicesAccess) +{ + Array<int> values = {4, 6, 2, 4, 8, 1, 10, 2, 5}; + InplacePriorityQueue<int> priority_queue(values); + + EXPECT_EQ(priority_queue.active_indices().size(), 9); + EXPECT_EQ(priority_queue.inactive_indices().size(), 0); + EXPECT_EQ(priority_queue.all_indices().size(), 9); + EXPECT_EQ(priority_queue.pop(), 10); + EXPECT_EQ(priority_queue.active_indices().size(), 8); + EXPECT_EQ(priority_queue.inactive_indices().size(), 1); + EXPECT_EQ(values[priority_queue.inactive_indices()[0]], 10); + EXPECT_EQ(priority_queue.all_indices().size(), 9); + EXPECT_EQ(priority_queue.pop(), 8); + EXPECT_EQ(priority_queue.inactive_indices().size(), 2); + EXPECT_EQ(values[priority_queue.inactive_indices()[0]], 8); + EXPECT_EQ(values[priority_queue.inactive_indices()[1]], 10); + EXPECT_EQ(priority_queue.all_indices().size(), 9); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_math_rotation_test.cc b/source/blender/blenlib/tests/BLI_math_rotation_test.cc index 02257ba83dd..5a179bff3d6 100644 --- a/source/blender/blenlib/tests/BLI_math_rotation_test.cc +++ b/source/blender/blenlib/tests/BLI_math_rotation_test.cc @@ -2,6 +2,7 @@ #include "testing/testing.h" +#include "BLI_math_base.h" #include "BLI_math_rotation.h" #include <cmath> @@ -71,6 +72,12 @@ TEST(math_rotation, quat_to_mat_to_quat_bad_T83196) test_quat_to_mat_to_quat(0.0149f, 0.9996f, -0.0212f, -0.0107f); } +TEST(math_rotation, quat_to_mat_to_quat_bad_negative) +{ + /* This shouldn't produce a negative q[0]. */ + test_quat_to_mat_to_quat(0.5f - 1e-6f, 0, -sqrtf(3) / 2 - 1e-6f, 0); +} + TEST(math_rotation, quat_to_mat_to_quat_near_1000) { test_quat_to_mat_to_quat(0.9999f, 0.01f, -0.001f, -0.01f); @@ -126,3 +133,17 @@ TEST(math_rotation, quat_to_mat_to_quat_near_0001) test_quat_to_mat_to_quat(0.25f, -0.025f, -0.25f, 0.97f); test_quat_to_mat_to_quat(0.30f, -0.030f, -0.30f, 0.95f); } + +TEST(math_rotation, quat_split_swing_and_twist_negative) +{ + const float input[4] = {-0.5f, 0, sqrtf(3) / 2, 0}; + const float expected_swing[4] = {1.0f, 0, 0, 0}; + const float expected_twist[4] = {0.5f, 0, -sqrtf(3) / 2, 0}; + float swing[4], twist[4]; + + float twist_angle = quat_split_swing_and_twist(input, 1, swing, twist); + + EXPECT_NEAR(twist_angle, -M_PI * 2 / 3, FLT_EPSILON); + EXPECT_V4_NEAR(swing, expected_swing, FLT_EPSILON); + EXPECT_V4_NEAR(twist, expected_twist, FLT_EPSILON); +} diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc index fcef2f8688a..23415e69b04 100644 --- a/source/blender/blenlib/tests/BLI_memory_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc @@ -158,4 +158,15 @@ static_assert(is_convertible_pointer_v<int **, int **const>); static_assert(is_convertible_pointer_v<int **, int *const *>); static_assert(is_convertible_pointer_v<int **, int const *const *>); +static_assert(is_span_convertible_pointer_v<int *, int *>); +static_assert(is_span_convertible_pointer_v<int *, const int *>); +static_assert(!is_span_convertible_pointer_v<const int *, int *>); +static_assert(is_span_convertible_pointer_v<const int *, const int *>); +static_assert(is_span_convertible_pointer_v<const int *, const void *>); +static_assert(!is_span_convertible_pointer_v<const int *, void *>); +static_assert(is_span_convertible_pointer_v<int *, void *>); +static_assert(is_span_convertible_pointer_v<int *, const void *>); +static_assert(!is_span_convertible_pointer_v<TestBaseClass *, TestChildClass *>); +static_assert(!is_span_convertible_pointer_v<TestChildClass *, TestBaseClass *>); + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc index 9a8d9df7873..d1c9f312b97 100644 --- a/source/blender/blenlib/tests/BLI_span_test.cc +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -337,4 +337,19 @@ TEST(span, MutableReverseIterator) EXPECT_EQ_ARRAY(src.data(), Span({14, 15, 16, 17}).data(), 4); } +TEST(span, constexpr_) +{ + static constexpr std::array<int, 3> src = {3, 2, 1}; + constexpr Span<int> span(src); + BLI_STATIC_ASSERT(span[2] == 1, ""); + BLI_STATIC_ASSERT(span.size() == 3, ""); + BLI_STATIC_ASSERT(span.slice(1, 2).size() == 2, ""); + BLI_STATIC_ASSERT(span.has_duplicates__linear_search() == false, ""); + + std::integral_constant<bool, span.first_index(1) == 2> ic; + BLI_STATIC_ASSERT(ic.value, ""); + + EXPECT_EQ(span.slice(1, 2).size(), 2); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_string_ref_test.cc b/source/blender/blenlib/tests/BLI_string_ref_test.cc index 2d488feff71..401a7bc1118 100644 --- a/source/blender/blenlib/tests/BLI_string_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_string_ref_test.cc @@ -298,4 +298,12 @@ TEST(string_ref, ToStringView) EXPECT_EQ(view, "hello"); } +TEST(string_ref, constexpr_) +{ + constexpr StringRef sref("World"); + BLI_STATIC_ASSERT(sref[2] == 'r', ""); + BLI_STATIC_ASSERT(sref.size() == 5, ""); + std::array<int, static_cast<std::size_t>(sref.find_first_of('o'))> compiles = {1}; + EXPECT_EQ(compiles[0], 1); +} } // namespace blender::tests diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index c4480e2c544..ea0532d884b 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -160,6 +160,7 @@ void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_p void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr); void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr); void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr); +void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr); void BLO_write_float3_array(BlendWriter *writer, uint num, const float *data_ptr); void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr); void BLO_write_string(BlendWriter *writer, const char *data_ptr); diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 0ab9a5e9e14..1d7c5d8a1d3 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -119,12 +119,20 @@ void BLO_blendfiledata_free(BlendFileData *bfd); /** \name BLO Blend File Handle API * \{ */ +struct BLODataBlockInfo { + char name[64]; /* MAX_NAME */ + struct AssetMetaData *asset_data; +}; + BlendHandle *BLO_blendhandle_from_file(const char *filepath, struct ReportList *reports); BlendHandle *BLO_blendhandle_from_memory(const void *mem, int memsize); struct LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, int ofblocktype, int *tot_names); +struct LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, + int ofblocktype, + int *tot_info_items); struct LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *tot_prev); struct LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh); diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index 1aecba5ba90..296480fc2e4 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -40,6 +40,7 @@ #include "DNA_genfile.h" #include "DNA_sdna_types.h" +#include "BKE_icons.h" #include "BKE_idtype.h" #include "BKE_main.h" @@ -160,6 +161,51 @@ LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, int ofblocktype, } /** + * Gets the names and asset-data (if ID is an asset) of all the data-blocks in a file of a certain + * type (e.g. all the scene names in a file). + * + * \param bh: The blendhandle to access. + * \param ofblocktype: The type of names to get. + * \param tot_info_items: The length of the returned list. + * \return A BLI_linklist of BLODataBlockInfo *. The links and #BLODataBlockInfo.asset_data should + * be freed with MEM_freeN. + */ +LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, int ofblocktype, int *tot_info_items) +{ + FileData *fd = (FileData *)bh; + LinkNode *infos = NULL; + BHead *bhead; + int tot = 0; + + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == ofblocktype) { + struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__); + const char *name = blo_bhead_id_name(fd, bhead) + 2; + + STRNCPY(info->name, name); + + /* Lastly, read asset data from the following blocks. */ + info->asset_data = blo_bhead_id_asset_data_address(fd, bhead); + if (info->asset_data) { + bhead = blo_read_asset_data_block(fd, bhead, &info->asset_data); + /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the next + * non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */ + bhead = blo_bhead_prev(fd, bhead); + } + + BLI_linklist_prepend(&infos, info); + tot++; + } + else if (bhead->code == ENDB) { + break; + } + } + + *tot_info_items = tot; + return infos; +} + +/** * Gets the previews of all the data-blocks in a file of a certain type * (e.g. all the scene previews in a file). * @@ -203,6 +249,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to if (looking) { if (bhead->SDNAnr == DNA_struct_find_nr(fd->filesdna, "PreviewImage")) { prv = BLO_library_read_struct(fd, bhead, "PreviewImage"); + if (prv) { memcpy(new_prv, prv, sizeof(PreviewImage)); if (prv->rect[0] && prv->w[0] && prv->h[0]) { @@ -217,6 +264,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to new_prv->rect[0] = NULL; new_prv->w[0] = new_prv->h[0] = 0; } + BKE_previewimg_finish(new_prv, 0); if (prv->rect[1] && prv->w[1] && prv->h[1]) { bhead = blo_bhead_next(fd, bhead); @@ -230,6 +278,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to new_prv->rect[1] = NULL; new_prv->w[1] = new_prv->h[1] = 0; } + BKE_previewimg_finish(new_prv, 1); MEM_freeN(prv); } } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 9ce767b7ce1..bccc7150523 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -44,7 +44,9 @@ #define DNA_DEPRECATED_ALLOW #include "DNA_anim_types.h" +#include "DNA_asset_types.h" #include "DNA_cachefile_types.h" +#include "DNA_collection_types.h" #include "DNA_fileglobal_types.h" #include "DNA_genfile.h" #include "DNA_key_types.h" @@ -53,6 +55,7 @@ #include "DNA_packedFile_types.h" #include "DNA_sdna_types.h" #include "DNA_sound_types.h" +#include "DNA_vfont_types.h" #include "DNA_volume_types.h" #include "DNA_workspace_types.h" @@ -71,6 +74,7 @@ #include "BKE_anim_data.h" #include "BKE_animsys.h" +#include "BKE_asset.h" #include "BKE_collection.h" #include "BKE_global.h" /* for G */ #include "BKE_idprop.h" @@ -955,12 +959,21 @@ static BHead *blo_bhead_read_full(FileData *fd, BHead *thisblock) } #endif /* USE_BHEAD_READ_ON_DEMAND */ -/* Warning! Caller's responsibility to ensure given bhead **is** and ID one! */ +/* Warning! Caller's responsibility to ensure given bhead **is** an ID one! */ const char *blo_bhead_id_name(const FileData *fd, const BHead *bhead) { return (const char *)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_name_offs); } +/* Warning! Caller's responsibility to ensure given bhead **is** an ID one! */ +AssetMetaData *blo_bhead_id_asset_data_address(const FileData *fd, const BHead *bhead) +{ + BLI_assert(BKE_idtype_idcode_is_valid(bhead->code)); + return (fd->id_asset_data_offs >= 0) ? + *(AssetMetaData **)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_asset_data_offs) : + NULL; +} + static void decode_blender_header(FileData *fd) { char header[SIZEOFBLENDERHEADER], num[4]; @@ -1038,6 +1051,8 @@ static bool read_file_dna(FileData *fd, const char **r_error_message) /* used to retrieve ID names from (bhead+1) */ fd->id_name_offs = DNA_elem_offset(fd->filesdna, "ID", "char", "name[]"); BLI_assert(fd->id_name_offs != -1); + fd->id_asset_data_offs = DNA_elem_offset( + fd->filesdna, "ID", "AssetMetaData", "*asset_data"); return true; } @@ -2358,6 +2373,11 @@ static void direct_link_id_common( return; } + if (id->asset_data) { + BLO_read_data_address(reader, &id->asset_data); + BKE_asset_metadata_read(reader, id->asset_data); + } + /*link direct data of ID properties*/ if (id->properties) { BLO_read_data_address(reader, &id->properties); @@ -2731,6 +2751,7 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, SpaceFile *sfile = (SpaceFile *)sl; sfile->op = NULL; sfile->previews_timer = NULL; + sfile->tags = FILE_TAG_REBUILD_MAIN_FILES; } else if (sl->spacetype == SPACE_ACTION) { SpaceAction *saction = (SpaceAction *)sl; @@ -3606,6 +3627,27 @@ static BHead *read_libblock(FileData *fd, /** \} */ /* -------------------------------------------------------------------- */ +/** \name Read Asset Data + * \{ */ + +BHead *blo_read_asset_data_block(FileData *fd, BHead *bhead, AssetMetaData **r_asset_data) +{ + BLI_assert(BKE_idtype_idcode_is_valid(bhead->code)); + + bhead = read_data_into_datamap(fd, bhead, "asset-data read"); + + BlendDataReader reader = {fd}; + BLO_read_data_address(&reader, r_asset_data); + BKE_asset_metadata_read(&reader, *r_asset_data); + + oldnewmap_clear(fd->datamap); + + return bhead; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Read Global Data * \{ */ @@ -3863,6 +3905,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) BLO_read_list(reader, &user->user_menus); BLO_read_list(reader, &user->addons); BLO_read_list(reader, &user->autoexec_paths); + BLO_read_list(reader, &user->asset_libraries); LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) { keymap->modal_items = NULL; diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index fb950e37da8..c724cc32051 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -34,16 +34,13 @@ #include "zlib.h" struct BLOCacheStorage; -struct GSet; struct IDNameLib_Map; struct Key; struct MemFile; struct Object; struct OldNewMap; -struct PartEff; struct ReportList; struct UserDef; -struct View3D; typedef struct IDNameLib_Map IDNameLib_Map; @@ -112,6 +109,9 @@ typedef struct FileData { int fileversion; /** Used to retrieve ID names from (bhead+1). */ int id_name_offs; + /** Used to retrieve asset data from (bhead+1). NOTE: This may not be available in old files, + * will be -1 then! */ + int id_asset_data_offs; /** For do_versions patching. */ int globalf, fileflags; @@ -159,6 +159,8 @@ void blo_end_packed_pointer_map(FileData *fd, struct Main *oldmain); void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd); void blo_make_old_idmap_from_main(FileData *fd, struct Main *bmain); +BHead *blo_read_asset_data_block(FileData *fd, BHead *bhead, struct AssetMetaData **r_asset_data); + void blo_cache_storage_init(FileData *fd, struct Main *bmain); void blo_cache_storage_old_bmain_clear(FileData *fd, struct Main *bmain_old); void blo_cache_storage_end(FileData *fd); @@ -170,6 +172,7 @@ BHead *blo_bhead_next(FileData *fd, BHead *thisblock); BHead *blo_bhead_prev(FileData *fd, BHead *thisblock); const char *blo_bhead_id_name(const FileData *fd, const BHead *bhead); +struct AssetMetaData *blo_bhead_id_asset_data_address(const FileData *fd, const BHead *bhead); /* do versions stuff */ diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index c86ad639216..b282a978e8a 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -46,6 +46,7 @@ #include "DNA_meshdata_types.h" #include "DNA_node_types.h" #include "DNA_object_fluidsim_types.h" +#include "DNA_object_force_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_sdna_types.h" diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index c33f2a8cad5..c336239ec59 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -28,11 +28,13 @@ #include "DNA_camera_types.h" #include "DNA_cloth_types.h" #include "DNA_constraint_types.h" +#include "DNA_dynamicpaint_types.h" #include "DNA_fluid_types.h" #include "DNA_genfile.h" #include "DNA_key_types.h" #include "DNA_light_types.h" #include "DNA_linestyle_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_fluidsim_types.h" diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index adc2b55b350..046749e4691 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -64,6 +64,7 @@ #include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_tracking.h" +#include "DNA_material_types.h" #include "SEQ_sequencer.h" @@ -301,7 +302,9 @@ static void do_version_hue_sat_node(bNodeTree *ntree, bNode *node) /* Take care of possible animation. */ AnimData *adt = BKE_animdata_from_id(&ntree->id); if (adt != NULL && adt->action != NULL) { - const char *prefix = BLI_sprintfN("nodes[\"%s\"]", node->name); + char node_name_esc[sizeof(node->name) * 2]; + BLI_str_escape(node_name_esc, node->name, sizeof(node_name_esc)); + const char *prefix = BLI_sprintfN("nodes[\"%s\"]", node_name_esc); for (FCurve *fcu = adt->action->curves.first; fcu != NULL; fcu = fcu->next) { if (STRPREFIX(fcu->rna_path, prefix)) { anim_change_prop_name(fcu, prefix, "color_hue", "inputs[1].default_value"); diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index a98e7c46f10..4df681002a0 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -29,6 +29,7 @@ #include "DNA_anim_types.h" #include "DNA_brush_types.h" #include "DNA_cachefile_types.h" +#include "DNA_collection_types.h" #include "DNA_constraint_types.h" #include "DNA_fluid_types.h" #include "DNA_genfile.h" @@ -52,6 +53,7 @@ #include "BKE_armature.h" #include "BKE_collection.h" #include "BKE_colortools.h" +#include "BKE_cryptomatte.h" #include "BKE_fcurve.h" #include "BKE_gpencil.h" #include "BKE_lib_id.h" @@ -72,6 +74,29 @@ /* Make preferences read-only, use versioning_userdef.c. */ #define U (*((const UserDef *)&U)) +static eSpaceSeq_Proxy_RenderSize get_sequencer_render_size(Main *bmain) +{ + eSpaceSeq_Proxy_RenderSize render_size = 100; + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + switch (sl->spacetype) { + case SPACE_SEQ: { + SpaceSeq *sseq = (SpaceSeq *)sl; + if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { + render_size = sseq->render_size; + break; + } + } + } + } + } + } + + return render_size; +} + /* image_size is width or height depending what RNA property is converted - X or Y. */ static void seq_convert_transform_animation(const Scene *scene, const char *path, @@ -210,6 +235,84 @@ static void seq_convert_transform_crop_lb(const Scene *scene, } } +static void seq_convert_transform_animation_2(const Scene *scene, + const char *path, + const float scale_to_fit_factor) +{ + if (scene->adt == NULL || scene->adt->action == NULL) { + return; + } + + FCurve *fcu = BKE_fcurve_find(&scene->adt->action->curves, path, 0); + if (fcu != NULL && !BKE_fcurve_is_empty(fcu)) { + BezTriple *bezt = fcu->bezt; + for (int i = 0; i < fcu->totvert; i++, bezt++) { + /* Same math as with old_image_center_*, but simplified. */ + bezt->vec[1][1] *= scale_to_fit_factor; + } + } +} + +static void seq_convert_transform_crop_2(const Scene *scene, + Sequence *seq, + const eSpaceSeq_Proxy_RenderSize render_size) +{ + const StripElem *s_elem = SEQ_render_give_stripelem(seq, seq->start); + if (s_elem == NULL) { + return; + } + + StripCrop *c = seq->strip->crop; + StripTransform *t = seq->strip->transform; + int image_size_x = s_elem->orig_width; + int image_size_y = s_elem->orig_height; + + if (SEQ_can_use_proxy(seq, SEQ_rendersize_to_proxysize(render_size))) { + image_size_x /= SEQ_rendersize_to_scale_factor(render_size); + image_size_y /= SEQ_rendersize_to_scale_factor(render_size); + } + + /* Calculate scale factor, so image fits in preview area with original aspect ratio. */ + const float scale_to_fit_factor = MIN2((float)scene->r.xsch / (float)image_size_x, + (float)scene->r.ysch / (float)image_size_y); + t->scale_x *= scale_to_fit_factor; + t->scale_y *= scale_to_fit_factor; + c->top /= scale_to_fit_factor; + c->bottom /= scale_to_fit_factor; + c->left /= scale_to_fit_factor; + c->right /= scale_to_fit_factor; + + char name_esc[(sizeof(seq->name) - 2) * 2], *path; + BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); + path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.min_x", name_esc); + seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor); + MEM_freeN(path); + path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.max_x", name_esc); + seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor); + MEM_freeN(path); + path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.min_y", name_esc); + seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor); + MEM_freeN(path); + path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.max_x", name_esc); + seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor); + MEM_freeN(path); +} + +static void seq_convert_transform_crop_lb_2(const Scene *scene, + const ListBase *lb, + const eSpaceSeq_Proxy_RenderSize render_size) +{ + + LISTBASE_FOREACH (Sequence *, seq, lb) { + if (seq->type != SEQ_TYPE_SOUND_RAM) { + seq_convert_transform_crop_2(scene, seq, render_size); + } + if (seq->type == SEQ_TYPE_META) { + seq_convert_transform_crop_lb_2(scene, &seq->seqbase, render_size); + } + } +} + void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) { if (!MAIN_VERSION_ATLEAST(bmain, 290, 1)) { @@ -439,25 +542,35 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) if (!MAIN_VERSION_ATLEAST(bmain, 292, 2)) { - eSpaceSeq_Proxy_RenderSize render_size = 100; + eSpaceSeq_Proxy_RenderSize render_size = get_sequencer_render_size(bmain); - for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { - switch (sl->spacetype) { - case SPACE_SEQ: { - SpaceSeq *sseq = (SpaceSeq *)sl; - render_size = sseq->render_size; - break; - } - } - } + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != NULL) { + seq_convert_transform_crop_lb(scene, &scene->ed->seqbase, render_size); } } + } + if (!MAIN_VERSION_ATLEAST(bmain, 292, 8)) { + /* Systematically rebuild posebones to ensure consistent ordering matching the one of bones in + * Armature obdata. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->type == OB_ARMATURE) { + BKE_pose_rebuild(bmain, ob, ob->data, true); + } + } + + /* Wet Paint Radius Factor */ + for (Brush *br = bmain->brushes.first; br; br = br->id.next) { + if (br->ob_mode & OB_MODE_SCULPT && br->wet_paint_radius_factor == 0.0f) { + br->wet_paint_radius_factor = 1.0f; + } + } + + eSpaceSeq_Proxy_RenderSize render_size = get_sequencer_render_size(bmain); LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (scene->ed != NULL) { - seq_convert_transform_crop_lb(scene, &scene->ed->seqbase, render_size); + seq_convert_transform_crop_lb_2(scene, &scene->ed->seqbase, render_size); } } } @@ -474,21 +587,6 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) */ { /* Keep this block, even when empty. */ - - /* Systematically rebuild posebones to ensure consistent ordering matching the one of bones in - * Armature obdata. */ - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->type == OB_ARMATURE) { - BKE_pose_rebuild(bmain, ob, ob->data, true); - } - } - } - - /* Wet Paint Radius Factor */ - for (Brush *br = bmain->brushes.first; br; br = br->id.next) { - if (br->ob_mode & OB_MODE_SCULPT && br->wet_paint_radius_factor == 0.0f) { - br->wet_paint_radius_factor = 1.0f; - } } } @@ -1232,6 +1330,93 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 292, 7)) { + /* Make all IDProperties used as interface of geometry node trees overridable. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + IDProperty *nmd_properties = nmd->settings.properties; + + BLI_assert(nmd_properties->type == IDP_GROUP); + LISTBASE_FOREACH (IDProperty *, nmd_socket_idprop, &nmd_properties->data.group) { + nmd_socket_idprop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY; + } + } + } + } + + /* EEVEE/Cycles Volumes consistency */ + for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + /* Remove Volume Transmittance render pass from each view layer. */ + LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { + view_layer->eevee.render_passes &= ~EEVEE_RENDER_PASS_UNUSED_8; + } + + /* Rename Renderlayer Socket `VolumeScatterCol` to `VolumeDir` */ + if (scene->nodetree) { + LISTBASE_FOREACH (bNode *, node, &scene->nodetree->nodes) { + if (node->type == CMP_NODE_R_LAYERS) { + LISTBASE_FOREACH (bNodeSocket *, output_socket, &node->outputs) { + const char *volume_scatter = "VolumeScatterCol"; + if (STREQLEN(output_socket->name, volume_scatter, MAX_NAME)) { + BLI_strncpy(output_socket->name, RE_PASSNAME_VOLUME_LIGHT, MAX_NAME); + } + } + } + } + } + } + + /* Convert `NodeCryptomatte->storage->matte_id` to `NodeCryptomatte->storage->entries` */ + if (!DNA_struct_find(fd->filesdna, "CryptomatteEntry")) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->nodetree) { + LISTBASE_FOREACH (bNode *, node, &scene->nodetree->nodes) { + if (node->type == CMP_NODE_CRYPTOMATTE) { + NodeCryptomatte *storage = (NodeCryptomatte *)node->storage; + char *matte_id = storage->matte_id; + if (matte_id == NULL || strlen(storage->matte_id) == 0) { + continue; + } + BKE_cryptomatte_matte_id_to_entries(NULL, storage, storage->matte_id); + MEM_SAFE_FREE(storage->matte_id); + } + } + } + } + } + + /* Overlay elements in the sequencer. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->flag |= (SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_SOURCE | + SEQ_SHOW_STRIP_DURATION); + } + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 292, 8)) { + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (STREQ(node->idname, "GeometryNodeRandomAttribute")) { + STRNCPY(node->idname, "GeometryNodeAttributeRandomize"); + } + } + } + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != NULL) { + scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init(); + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index 19e392734f0..631abe10ddc 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -840,12 +840,14 @@ static void update_mapping_node_fcurve_rna_path_callback(ID *UNUSED(id), fcurve->rna_path = BLI_sprintfN("%s.%s", data->nodePath, "inputs[3].default_value"); } else if (data->minimumNode && BLI_str_endswith(old_fcurve_rna_path, "max")) { - fcurve->rna_path = BLI_sprintfN( - "nodes[\"%s\"].%s", data->minimumNode->name, "inputs[1].default_value"); + char node_name_esc[sizeof(data->minimumNode->name) * 2]; + BLI_str_escape(node_name_esc, data->minimumNode->name, sizeof(node_name_esc)); + fcurve->rna_path = BLI_sprintfN("nodes[\"%s\"].%s", node_name_esc, "inputs[1].default_value"); } else if (data->maximumNode && BLI_str_endswith(old_fcurve_rna_path, "min")) { - fcurve->rna_path = BLI_sprintfN( - "nodes[\"%s\"].%s", data->maximumNode->name, "inputs[1].default_value"); + char node_name_esc[sizeof(data->maximumNode->name) * 2]; + BLI_str_escape(node_name_esc, data->maximumNode->name, sizeof(node_name_esc)); + fcurve->rna_path = BLI_sprintfN("nodes[\"%s\"].%s", node_name_esc, "inputs[1].default_value"); } if (fcurve->rna_path != old_fcurve_rna_path) { @@ -955,7 +957,10 @@ static void update_mapping_node_inputs_and_properties(bNodeTree *ntree) MEM_freeN(node->storage); node->storage = NULL; - char *nodePath = BLI_sprintfN("nodes[\"%s\"]", node->name); + char node_name_esc[sizeof(node->name) * 2]; + BLI_str_escape(node_name_esc, node->name, sizeof(node_name_esc)); + + char *nodePath = BLI_sprintfN("nodes[\"%s\"]", node_name_esc); MappingNodeFCurveCallbackData data = {nodePath, minimumNode, maximumNode}; BKE_fcurves_id_cb(&ntree->id, update_mapping_node_fcurve_rna_path_callback, &data); MEM_freeN(nodePath); diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 48a24755250..198f65b9794 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -37,6 +37,7 @@ #include "DNA_curveprofile_types.h" #include "DNA_gpencil_types.h" #include "DNA_light_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -181,7 +182,8 @@ static void blo_update_defaults_screen(bScreen *screen, } else if (area->spacetype == SPACE_SEQ) { SpaceSeq *seq = area->spacedata.first; - seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT; + seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | + SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_DURATION; } else if (area->spacetype == SPACE_TEXT) { /* Show syntax and line numbers in Script workspace text editor. */ @@ -737,6 +739,14 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_ERASER; } + brush_name = "Multires Displacement Smear"; + brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); + if (!brush) { + brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT); + id_us_min(&brush->id); + brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_SMEAR; + } + /* Use the same tool icon color in the brush cursor */ for (brush = bmain->brushes.first; brush; brush = brush->id.next) { if (brush->ob_mode & OB_MODE_SCULPT) { diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index d654a0e30bd..6f459339675 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -49,6 +49,7 @@ #include "DNA_nla_types.h" #include "DNA_node_types.h" #include "DNA_object_fluidsim_types.h" +#include "DNA_object_force_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_sdna_types.h" diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 7bc11317bb4..f1572c563bf 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -31,6 +31,7 @@ #endif #include "DNA_anim_types.h" +#include "DNA_collection_types.h" #include "DNA_curve_types.h" #include "DNA_scene_types.h" #include "DNA_space_types.h" @@ -43,6 +44,7 @@ #include "BKE_idprop.h" #include "BKE_keyconfig.h" #include "BKE_main.h" +#include "BKE_preferences.h" #include "BLO_readfile.h" @@ -328,9 +330,6 @@ void blo_do_versions_userdef(UserDef *userdef) #define USER_VERSION_ATLEAST(ver, subver) MAIN_VERSION_ATLEAST(userdef, ver, subver) /* the UserDef struct is not corrected with do_versions() .... ugh! */ - if (userdef->wheellinescroll == 0) { - userdef->wheellinescroll = 3; - } if (userdef->menuthreshold1 == 0) { userdef->menuthreshold1 = 5; userdef->menuthreshold2 = 2; @@ -832,6 +831,9 @@ void blo_do_versions_userdef(UserDef *userdef) */ { /* Keep this block, even when empty. */ + if (BLI_listbase_is_empty(&userdef->asset_libraries)) { + BKE_preferences_asset_library_default_add(userdef); + } } LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) { diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 11fe240620a..0a4f2fde93f 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -93,6 +93,7 @@ /* allow writefile to use deprecated functionality (for forward compatibility code) */ #define DNA_DEPRECATED_ALLOW +#include "DNA_collection_types.h" #include "DNA_fileglobal_types.h" #include "DNA_genfile.h" #include "DNA_sdna_types.h" @@ -755,6 +756,10 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) BLO_write_struct(writer, bPathCompare, path_cmp); } + LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library, &userdef->asset_libraries) { + BLO_write_struct(writer, bUserAssetLibrary, asset_library); + } + LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) { BLO_write_struct(writer, uiStyle, style); } @@ -1369,6 +1374,11 @@ void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr) BLO_write_raw(writer, sizeof(float) * (size_t)num, data_ptr); } +void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr) +{ + BLO_write_raw(writer, sizeof(double) * (size_t)num, data_ptr); +} + void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr) { BLO_write_raw(writer, sizeof(void *) * (size_t)num, data_ptr); diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc index bfb093c569f..ea5d66e195c 100644 --- a/source/blender/bmesh/tools/bmesh_boolean.cc +++ b/source/blender/bmesh/tools/bmesh_boolean.cc @@ -296,7 +296,7 @@ static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out, bool keep_hidden BMIter liter; BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf)); while (l != nullptr) { - BM_loop_interp_from_face(bm, l, orig_face, true, true); + BM_loop_interp_from_face(bm, l, orig_face, false, true); l = static_cast<BMLoop *>(BM_iter_step(&liter)); } } diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cpp b/source/blender/compositor/nodes/COM_CryptomatteNode.cpp index 8cc6b933759..7ca4e1f76fc 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cpp +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cpp @@ -17,12 +17,15 @@ */ #include "COM_CryptomatteNode.h" -#include "BLI_assert.h" -#include "BLI_hash_mm3.h" -#include "BLI_string.h" #include "COM_ConvertOperation.h" #include "COM_CryptomatteOperation.h" #include "COM_SetAlphaOperation.h" + +#include "BLI_assert.h" +#include "BLI_hash_mm3.h" +#include "BLI_listbase.h" +#include "BLI_string.h" + #include <iterator> CryptomatteNode::CryptomatteNode(bNode *editorNode) : Node(editorNode) @@ -30,24 +33,6 @@ CryptomatteNode::CryptomatteNode(bNode *editorNode) : Node(editorNode) /* pass */ } -/* This is taken from the Cryptomatte specification 1.0. */ -static inline float hash_to_float(uint32_t hash) -{ - uint32_t mantissa = hash & ((1 << 23) - 1); - uint32_t exponent = (hash >> 23) & ((1 << 8) - 1); - exponent = max(exponent, (uint32_t)1); - exponent = min(exponent, (uint32_t)254); - exponent = exponent << 23; - uint32_t sign = (hash >> 31); - sign = sign << 31; - uint32_t float_bits = sign | exponent | mantissa; - float f; - /* Bit casting relies on equal size for both types. */ - BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float and uint32_t are not the same size") - ::memcpy(&f, &float_bits, sizeof(float)); - return f; -} - void CryptomatteNode::convertToOperations(NodeConverter &converter, const CompositorContext & /*context*/) const { @@ -61,30 +46,8 @@ void CryptomatteNode::convertToOperations(NodeConverter &converter, CryptomatteOperation *operation = new CryptomatteOperation(getNumberOfInputSockets() - 1); if (cryptoMatteSettings) { - if (cryptoMatteSettings->matte_id) { - /* Split the string by commas, ignoring white space. */ - std::string input = cryptoMatteSettings->matte_id; - std::istringstream ss(input); - while (ss.good()) { - std::string token; - getline(ss, token, ','); - /* Ignore empty tokens. */ - if (token.length() > 0) { - size_t first = token.find_first_not_of(' '); - size_t last = token.find_last_not_of(' '); - if (first == std::string::npos || last == std::string::npos) { - break; - } - token = token.substr(first, (last - first + 1)); - if (*token.begin() == '<' && *(--token.end()) == '>') { - operation->addObjectIndex(atof(token.substr(1, token.length() - 2).c_str())); - } - else { - uint32_t hash = BLI_hash_mm3((const unsigned char *)token.c_str(), token.length(), 0); - operation->addObjectIndex(hash_to_float(hash)); - } - } - } + LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptoMatteSettings->entries) { + operation->addObjectIndex(cryptomatte_entry->encoded_hash); } } diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index f894bdabba4..4e618d8625d 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -39,7 +39,6 @@ struct Main; struct Object; struct Scene; struct Simulation; -struct ViewLayer; struct bNodeTree; #include "BLI_sys_types.h" diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 5af70305e13..e5301532ddc 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1483,6 +1483,9 @@ void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket) else if (socket->type == SOCK_IMAGE) { build_id((ID *)((bNodeSocketValueImage *)socket->default_value)->value); } + else if (socket->type == SOCK_COLLECTION) { + build_id((ID *)((bNodeSocketValueCollection *)socket->default_value)->value); + } } void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc index c7669b9fecb..b1fd86f13bc 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc @@ -34,6 +34,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "DNA_collection_types.h" #include "DNA_freestyle_types.h" #include "DNA_layer_types.h" #include "DNA_linestyle_types.h" diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 11d34782569..de68ec6210e 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -39,6 +39,7 @@ #include "DNA_armature_types.h" #include "DNA_cachefile_types.h" #include "DNA_camera_types.h" +#include "DNA_cloth_types.h" #include "DNA_collection_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" @@ -2316,6 +2317,12 @@ void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket) build_image(image); } } + else if (socket->type == SOCK_COLLECTION) { + Collection *collection = ((bNodeSocketValueCollection *)socket->default_value)->value; + if (collection != nullptr) { + build_collection(nullptr, nullptr, collection); + } + } } void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index cbfb51c59a6..5587379089c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -45,7 +45,6 @@ #include "intern/node/deg_node_id.h" #include "intern/node/deg_node_operation.h" -struct Base; struct CacheFile; struct Camera; struct Collection; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc index 8df8d4914c3..24876049942 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc @@ -34,6 +34,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "DNA_collection_types.h" #include "DNA_linestyle_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index b92bf475f49..e472d82f2ee 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -136,8 +136,8 @@ bool deg_iterator_components_step(BLI_Iterator *iter) return false; } - if (data->geometry_component_owner->type != OB_POINTCLOUD) { - /* Only point clouds support multiple geometry components currently. */ + if (data->geometry_component_owner->runtime.geometry_set_eval == nullptr) { + /* Return the object itself, if it does not have a geometry set yet. */ iter->current = data->geometry_component_owner; data->geometry_component_owner = nullptr; return true; @@ -149,10 +149,16 @@ bool deg_iterator_components_step(BLI_Iterator *iter) return false; } + /* The mesh component. */ if (data->geometry_component_id == 0) { data->geometry_component_id++; - /* The mesh component. */ + /* Don't use a temporary object for this component, when the owner is a mesh object. */ + if (data->geometry_component_owner->type == OB_MESH) { + iter->current = data->geometry_component_owner; + return true; + } + const Mesh *mesh = geometry_set->get_mesh_for_read(); if (mesh != nullptr) { Object *temp_object = &data->temp_geometry_component_object; @@ -164,10 +170,17 @@ bool deg_iterator_components_step(BLI_Iterator *iter) return true; } } + + /* The pointcloud component. */ if (data->geometry_component_id == 1) { data->geometry_component_id++; - /* The pointcloud component. */ + /* Don't use a temporary object for this component, when the owner is a point cloud object. */ + if (data->geometry_component_owner->type == OB_POINTCLOUD) { + iter->current = data->geometry_component_owner; + return true; + } + const PointCloud *pointcloud = geometry_set->get_pointcloud_for_read(); if (pointcloud != nullptr) { Object *temp_object = &data->temp_geometry_component_object; @@ -179,6 +192,7 @@ bool deg_iterator_components_step(BLI_Iterator *iter) return true; } } + data->geometry_component_owner = nullptr; return false; } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h index 1f58c54dbf4..ec661360fdf 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h @@ -25,8 +25,6 @@ #pragma once -struct Main; - namespace blender { namespace deg { diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 44ff86b3333..9150de7184a 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -125,7 +125,7 @@ void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata) return; } if (eevee_cryptomatte_active_layers(view_layer) != 0) { - g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE; + g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE | EEVEE_RENDER_PASS_VOLUME_LIGHT; g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0; } @@ -137,7 +137,8 @@ void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata), { EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_TextureList *txl = vedata->txl; - EEVEE_PrivateData *g_data = vedata->stl->g_data; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_PrivateData *g_data = stl->g_data; DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -303,7 +304,7 @@ void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *s GPUBatch *geom = DRW_cache_object_surface_get(ob); if (geom) { DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( - vedata, sldata, ob, false, NULL); + vedata, sldata, ob, NULL, false); DRW_shgroup_call(grp, geom, ob); } } @@ -469,6 +470,8 @@ static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata) { EEVEE_StorageList *stl = vedata->stl; EEVEE_PrivateData *g_data = stl->g_data; + EEVEE_EffectsInfo *effects = stl->effects; + EEVEE_TextureList *txl = vedata->txl; const DRWContextState *draw_ctx = DRW_context_state_get(); const ViewLayer *view_layer = draw_ctx->view_layer; const int num_cryptomatte_layers = eevee_cryptomatte_layers_count(view_layer); @@ -478,11 +481,25 @@ static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata) EEVEE_CryptomatteSample *accum_buffer = g_data->cryptomatte_accum_buffer; BLI_assert(accum_buffer); + float *volumetric_transmittance_buffer = NULL; + if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) { + volumetric_transmittance_buffer = GPU_texture_read( + txl->volume_transmittance_accum, GPU_DATA_FLOAT, 0); + } + const int num_samples = effects->taa_current_sample; + int accum_pixel_index = 0; int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer); for (int pixel_index = 0; pixel_index < buffer_size; pixel_index++, accum_pixel_index += accum_pixel_stride) { + float coverage = 1.0f; + if (volumetric_transmittance_buffer != NULL) { + coverage = (volumetric_transmittance_buffer[pixel_index * 4] + + volumetric_transmittance_buffer[pixel_index * 4 + 1] + + volumetric_transmittance_buffer[pixel_index * 4 + 2]) / + (3.0f * num_samples); + } for (int layer = 0; layer < num_cryptomatte_layers; layer++) { const int layer_offset = eevee_cryptomatte_layer_offset(view_layer, layer); /* Calculate the total weight of the sample. */ @@ -493,24 +510,40 @@ static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata) } BLI_assert(total_weight > 0.0f); - float total_weight_inv = 1.0f / total_weight; - for (int level = 0; level < num_levels; level++) { - EEVEE_CryptomatteSample *sample = &accum_buffer[accum_pixel_index + layer_offset + level]; - /* Remove background samples. These samples were used to determine the correct weight - * but won't be part of the final result. */ - if (sample->hash == 0.0f) { + float total_weight_inv = coverage / total_weight; + if (total_weight_inv > 0.0f) { + for (int level = 0; level < num_levels; level++) { + EEVEE_CryptomatteSample *sample = + &accum_buffer[accum_pixel_index + layer_offset + level]; + /* Remove background samples. These samples were used to determine the correct weight + * but won't be part of the final result. */ + if (sample->hash == 0.0f) { + sample->weight = 0.0f; + } + sample->weight *= total_weight_inv; + } + + /* Sort accum buffer by coverage of each sample. */ + qsort(&accum_buffer[accum_pixel_index + layer_offset], + num_levels, + sizeof(EEVEE_CryptomatteSample), + eevee_cryptomatte_sample_cmp_reverse); + } + else { + /* This pixel doesn't have any weight, so clear it fully. */ + for (int level = 0; level < num_levels; level++) { + EEVEE_CryptomatteSample *sample = + &accum_buffer[accum_pixel_index + layer_offset + level]; sample->weight = 0.0f; + sample->hash = 0.0f; } - sample->weight *= total_weight_inv; } - - /* Sort accum buffer by coverage of each sample. */ - qsort(&accum_buffer[accum_pixel_index + layer_offset], - num_levels, - sizeof(EEVEE_CryptomatteSample), - eevee_cryptomatte_sample_cmp_reverse); } } + + if (volumetric_transmittance_buffer) { + MEM_freeN(volumetric_transmittance_buffer); + } } /* Extract cryptomatte layer from the cryptomatte_accum_buffer to render passes. */ diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.h b/source/blender/draw/engines/eevee/eevee_lightcache.h index 17392c0de0b..fde0c80ab37 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.h +++ b/source/blender/draw/engines/eevee/eevee_lightcache.h @@ -24,14 +24,14 @@ #include "BLI_sys_types.h" /* for bool */ +struct BlendDataReader; +struct BlendWriter; struct EEVEE_Data; struct EEVEE_ViewLayerData; struct LightCache; struct Scene; struct SceneEEVEE; struct ViewLayer; -struct BlendWriter; -struct BlendDataReader; /* Light Bake */ struct wmJob *EEVEE_lightbake_job_create(struct wmWindowManager *wm, diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index a6a480ca967..5bf8cab1b22 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -989,6 +989,7 @@ typedef struct EEVEE_PrivateData { GPUTexture *renderpass_input; GPUTexture *renderpass_col_input; GPUTexture *renderpass_light_input; + GPUTexture *renderpass_transmittance_input; /* Renderpass ubo reference used by material pass. */ struct GPUUniformBuf *renderpass_ubo; /** For rendering shadows. */ diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index 2b2ff2e5c90..2a01aeefce8 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -461,21 +461,13 @@ static void eevee_render_result_environment(RenderLayer *rl, EEVEE_RENDER_RESULT_MATERIAL_PASS(ENVIRONMENT, ENVIRONMENT) } -static void eevee_render_result_volume_scatter(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) -{ - EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_SCATTER, VOLUME_SCATTER) -} -static void eevee_render_result_volume_transmittance(RenderLayer *rl, - const char *viewname, - const rcti *rect, - EEVEE_Data *vedata, - EEVEE_ViewLayerData *sldata) +static void eevee_render_result_volume_light(RenderLayer *rl, + const char *viewname, + const rcti *rect, + EEVEE_Data *vedata, + EEVEE_ViewLayerData *sldata) { - EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_TRANSMITTANCE, VOLUME_TRANSMITTANCE) + EEVEE_RENDER_RESULT_MATERIAL_PASS(VOLUME_LIGHT, VOLUME_LIGHT) } static void eevee_render_result_aovs(RenderLayer *rl, @@ -696,8 +688,7 @@ void EEVEE_render_read_result(EEVEE_Data *vedata, eevee_render_result_emission(rl, viewname, rect, vedata, sldata); eevee_render_result_environment(rl, viewname, rect, vedata, sldata); eevee_render_result_bloom(rl, viewname, rect, vedata, sldata); - eevee_render_result_volume_scatter(rl, viewname, rect, vedata, sldata); - eevee_render_result_volume_transmittance(rl, viewname, rect, vedata, sldata); + eevee_render_result_volume_light(rl, viewname, rect, vedata, sldata); eevee_render_result_aovs(rl, viewname, rect, vedata, sldata); eevee_render_result_cryptomatte(rl, viewname, rect, vedata, sldata); } @@ -730,8 +721,7 @@ void EEVEE_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *v CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB"); CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB"); CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_EEVEE(VOLUME_SCATTER, SOCK_RGBA, 3, "RGB"); - CHECK_PASS_EEVEE(VOLUME_TRANSMITTANCE, SOCK_RGBA, 3, "RGB"); + CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB"); CHECK_PASS_EEVEE(BLOOM, SOCK_RGBA, 3, "RGB"); LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index e7a03c678a8..dff3b437953 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -44,14 +44,14 @@ typedef enum eRenderPassPostProcessType { PASS_POST_AO = 6, PASS_POST_NORMAL = 7, PASS_POST_TWO_LIGHT_BUFFERS = 8, + PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR = 9, } eRenderPassPostProcessType; /* bitmask containing all renderpasses that need post-processing */ #define EEVEE_RENDERPASSES_WITH_POST_PROCESSING \ (EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_MIST | EEVEE_RENDER_PASS_NORMAL | \ - EEVEE_RENDER_PASS_AO | EEVEE_RENDER_PASS_BLOOM | EEVEE_RENDER_PASS_VOLUME_SCATTER | \ - EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE | EEVEE_RENDER_PASS_SHADOW | \ - EEVEE_RENDERPASSES_MATERIAL) + EEVEE_RENDER_PASS_AO | EEVEE_RENDER_PASS_BLOOM | EEVEE_RENDER_PASS_VOLUME_LIGHT | \ + EEVEE_RENDER_PASS_SHADOW | EEVEE_RENDERPASSES_MATERIAL) #define EEVEE_RENDERPASSES_ALL \ (EEVEE_RENDERPASSES_WITH_POST_PROCESSING | EEVEE_RENDER_PASS_COMBINED) @@ -64,7 +64,10 @@ typedef enum eRenderPassPostProcessType { EEVEE_RENDER_PASS_BLOOM) #define EEVEE_RENDERPASSES_LIGHT_PASS \ (EEVEE_RENDER_PASS_DIFFUSE_LIGHT | EEVEE_RENDER_PASS_SPECULAR_LIGHT) - +/* Render passes that uses volume transmittance when available */ +#define EEVEE_RENDERPASSES_USES_TRANSMITTANCE \ + (EEVEE_RENDER_PASS_DIFFUSE_COLOR | EEVEE_RENDER_PASS_SPECULAR_COLOR | EEVEE_RENDER_PASS_EMIT | \ + EEVEE_RENDER_PASS_ENVIRONMENT) bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata) { EEVEE_StorageList *stl = vedata->stl; @@ -147,6 +150,18 @@ void EEVEE_renderpasses_init(EEVEE_Data *vedata) EEVEE_cryptomatte_renderpasses_init(vedata); } +BLI_INLINE bool eevee_renderpasses_volumetric_active(const EEVEE_EffectsInfo *effects, + const EEVEE_PrivateData *g_data) +{ + if (effects->enabled_effects & EFFECT_VOLUMETRIC) { + if (g_data->render_passes & + (EEVEE_RENDER_PASS_VOLUME_LIGHT | EEVEE_RENDERPASSES_USES_TRANSMITTANCE)) { + return true; + } + } + return false; +} + void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples) @@ -189,8 +204,7 @@ void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata, EEVEE_bloom_output_init(sldata, vedata, tot_samples); } - if ((g_data->render_passes & - (EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE | EEVEE_RENDER_PASS_VOLUME_SCATTER)) != 0) { + if (eevee_renderpasses_volumetric_active(effects, g_data)) { EEVEE_volumes_output_init(sldata, vedata, tot_samples); } @@ -198,6 +212,7 @@ void EEVEE_renderpasses_output_init(EEVEE_ViewLayerData *sldata, g_data->renderpass_input = txl->color; g_data->renderpass_col_input = txl->color; g_data->renderpass_light_input = txl->color; + g_data->renderpass_transmittance_input = txl->color; } else { /* Free unneeded memory */ @@ -227,6 +242,8 @@ void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ve DRW_shgroup_uniform_texture_ref(grp, "inputColorBuffer", &g_data->renderpass_col_input); DRW_shgroup_uniform_texture_ref( grp, "inputSecondLightBuffer", &g_data->renderpass_light_input); + DRW_shgroup_uniform_texture_ref( + grp, "inputTransmittanceBuffer", &g_data->renderpass_transmittance_input); DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); DRW_shgroup_uniform_block_ref(grp, "common_block", &sldata->common_ubo); DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &sldata->renderpass_ubo.combined); @@ -265,6 +282,19 @@ void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata), g_data->renderpass_current_sample = current_sample; g_data->renderpass_type = renderpass_type; g_data->renderpass_postprocess = PASS_POST_UNDEFINED; + const bool volumetric_active = eevee_renderpasses_volumetric_active(effects, g_data); + eRenderPassPostProcessType default_color_pass_type = + volumetric_active ? PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR : PASS_POST_ACCUMULATED_COLOR; + g_data->renderpass_transmittance_input = volumetric_active ? txl->volume_transmittance_accum : + txl->color; + + if (!volumetric_active && renderpass_type == EEVEE_RENDER_PASS_VOLUME_LIGHT) { + /* Early exit: Volumetric effect is off, but the volume light pass was requested. */ + static float clear_col[4] = {0.0f}; + GPU_framebuffer_bind(fbl->renderpass_fb); + GPU_framebuffer_clear_color(fbl->renderpass_fb, clear_col); + return; + } switch (renderpass_type) { case EEVEE_RENDER_PASS_Z: { @@ -286,38 +316,33 @@ void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata), g_data->renderpass_input = txl->mist_accum; break; } - case EEVEE_RENDER_PASS_VOLUME_SCATTER: { + case EEVEE_RENDER_PASS_VOLUME_LIGHT: { g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR; g_data->renderpass_input = txl->volume_scatter_accum; break; } - case EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR; - g_data->renderpass_input = txl->volume_transmittance_accum; - break; - } case EEVEE_RENDER_PASS_SHADOW: { g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_VALUE; g_data->renderpass_input = txl->shadow_accum; break; } case EEVEE_RENDER_PASS_DIFFUSE_COLOR: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR; + g_data->renderpass_postprocess = default_color_pass_type; g_data->renderpass_input = txl->diff_color_accum; break; } case EEVEE_RENDER_PASS_SPECULAR_COLOR: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR; + g_data->renderpass_postprocess = default_color_pass_type; g_data->renderpass_input = txl->spec_color_accum; break; } case EEVEE_RENDER_PASS_ENVIRONMENT: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR; + g_data->renderpass_postprocess = default_color_pass_type; g_data->renderpass_input = txl->env_accum; break; } case EEVEE_RENDER_PASS_EMIT: { - g_data->renderpass_postprocess = PASS_POST_ACCUMULATED_COLOR; + g_data->renderpass_postprocess = default_color_pass_type; g_data->renderpass_input = txl->emit_accum; break; } @@ -372,7 +397,8 @@ void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata, { EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; - eViewLayerEEVEEPassType render_pass = stl->g_data->render_passes; + EEVEE_PrivateData *g_data = stl->g_data; + eViewLayerEEVEEPassType render_pass = g_data->render_passes; if (!post_effect) { if ((render_pass & EEVEE_RENDER_PASS_MIST) != 0) { @@ -387,8 +413,7 @@ void EEVEE_renderpasses_output_accumulate(EEVEE_ViewLayerData *sldata, if ((render_pass & EEVEE_RENDERPASSES_MATERIAL) != 0) { EEVEE_material_output_accumulate(sldata, vedata); } - if ((render_pass & - (EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE | EEVEE_RENDER_PASS_VOLUME_SCATTER)) != 0) { + if (eevee_renderpasses_volumetric_active(effects, g_data)) { EEVEE_volumes_output_accumulate(sldata, vedata); } if ((render_pass & EEVEE_RENDER_PASS_CRYPTOMATTE) != 0) { diff --git a/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl b/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl index 1e499dbf991..9426b8e4a7b 100644 --- a/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/cryptomatte_frag.glsl @@ -4,4 +4,4 @@ out vec4 fragColor; void main() { fragColor = cryptohash; -}
\ No newline at end of file +} diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl index eb6ca4b9de8..0bbbe58c44a 100644 --- a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl @@ -11,6 +11,7 @@ #define PASS_POST_AO 6 #define PASS_POST_NORMAL 7 #define PASS_POST_TWO_LIGHT_BUFFERS 8 +#define PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR 9 uniform int postProcessType; uniform int currentSample; @@ -19,6 +20,7 @@ uniform sampler2D depthBuffer; uniform sampler2D inputBuffer; uniform sampler2D inputSecondLightBuffer; uniform sampler2D inputColorBuffer; +uniform sampler2D inputTransmittanceBuffer; out vec4 fragColor; @@ -99,6 +101,11 @@ void main() vec4 accumulated_color = texelFetch(inputBuffer, texel, 0); color = (accumulated_color / currentSample); } + else if (postProcessType == PASS_POST_ACCUMULATED_TRANSMITTANCE_COLOR) { + vec3 accumulated_color = texelFetch(inputBuffer, texel, 0).rgb; + vec3 transmittance = texelFetch(inputTransmittanceBuffer, texel, 0).rgb; + color.rgb = (accumulated_color / currentSample) * (transmittance / currentSample); + } else if (postProcessType == PASS_POST_ACCUMULATED_LIGHT) { vec3 accumulated_light = texelFetch(inputBuffer, texel, 0).rgb; vec3 accumulated_color = texelFetch(inputColorBuffer, texel, 0).rgb; diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 04128dc157e..d0bd56b42dd 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -39,10 +39,7 @@ extern DrawEngineType draw_engine_gpencil_type; struct GPENCIL_Data; struct GPENCIL_StorageList; struct GPUBatch; -struct GPUVertBuf; -struct GPUVertFormat; struct GpencilBatchCache; -struct MaterialGPencilStyle; struct Object; struct RenderEngine; struct RenderLayer; diff --git a/source/blender/draw/engines/image/image_private.h b/source/blender/draw/engines/image/image_private.h index d5821cc5d70..76a94e68da1 100644 --- a/source/blender/draw/engines/image/image_private.h +++ b/source/blender/draw/engines/image/image_private.h @@ -25,11 +25,9 @@ extern "C" { #endif /* Forward declarations */ -struct GPUBatch; struct GPUTexture; struct ImBuf; struct Image; -struct rcti; /* *********** LISTS *********** */ diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_verts_frag.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_verts_frag.glsl index 1d936e4e072..ea2715a3c32 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_uv_verts_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_uv_verts_frag.glsl @@ -1,6 +1,5 @@ -uniform vec4 outlineColor; - +in vec4 outlineColor; in vec4 radii; in vec4 fillColor; out vec4 fragColor; diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_verts_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_verts_vert.glsl index 9e9df82a77d..cb70a3b433c 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_uv_verts_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_uv_verts_vert.glsl @@ -26,7 +26,7 @@ void main() /* Move selected vertices to the top * Vertices are between 0.0 and 0.2, Edges between 0.2 and 0.4 * actual pixels are at 0.75, 1.0 is used for the background. */ - float depth = is_selected ? 0.05 : 0.15; + float depth = is_selected ? (is_pinned ? 0.05 : 0.10) : 0.15; gl_Position = vec4(point_world_to_ndc(world_pos).xy, depth, 1.0); gl_PointSize = pointSize; diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index fbe71900915..4df2ba1e913 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -64,7 +64,6 @@ #include "DEG_depsgraph.h" struct GPUBatch; -struct GPUFrameBuffer; struct GPUMaterial; struct GPUShader; struct GPUTexture; diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index 75685c7e2f0..a9febcedbf9 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -38,6 +38,7 @@ #include "DNA_modifier_types.h" #include "DNA_particle_types.h" +#include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_particle.h" #include "BKE_pointcache.h" diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 23dd47d4ab5..a059ac32311 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -22,10 +22,8 @@ #pragma once -struct DRWPass; struct DRWShadingGroup; struct FluidModifierData; -struct GPUMaterial; struct ModifierData; struct Object; struct ParticleSystem; diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index f1598ea2fff..ba03cee8149 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -669,10 +669,14 @@ static void drw_uniform_attribute_lookup(GPUUniformAttr *attr, DupliObject *dupli_source, float r_data[4]) { - char idprop_name[sizeof(attr->name) + 4]; - copy_v4_fl(r_data, 0); - sprintf(idprop_name, "[\"%s\"]", attr->name); + + char idprop_name[(sizeof(attr->name) * 2) + 4]; + { + char attr_name_esc[sizeof(attr->name) * 2]; + BLI_str_escape(attr_name_esc, attr->name, sizeof(attr_name_esc)); + SNPRINTF(idprop_name, "[\"%s\"]", attr_name_esc); + } /* If requesting instance data, check the parent particle system and object. */ if (attr->use_dupli) { diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 988975bd399..ffc565d0514 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2073,7 +2073,6 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, * for the image editor this is when showing UV's.*/ const bool do_populate_loop = (DST.draw_ctx.space_data->spacetype == SPACE_IMAGE); const bool do_annotations = drw_draw_show_annotation(); - const bool do_region_callbacks = (DST.draw_ctx.space_data->spacetype != SPACE_IMAGE); const bool do_draw_gizmos = (DST.draw_ctx.space_data->spacetype != SPACE_IMAGE); /* Get list of enabled engines */ @@ -2125,7 +2124,7 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, /* Start Drawing */ DRW_state_reset(); - if (do_region_callbacks && DST.draw_ctx.evil_C) { + if (DST.draw_ctx.evil_C) { ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_PRE_VIEW); } @@ -2147,10 +2146,8 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, if (do_annotations) { ED_annotation_draw_view2d(DST.draw_ctx.evil_C, true); } - if (do_region_callbacks) { - GPU_depth_test(GPU_DEPTH_NONE); - ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW); - } + GPU_depth_test(GPU_DEPTH_NONE); + ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW); GPU_matrix_pop_projection(); /* Callback can be nasty and do whatever they want with the state. * Don't trust them! */ diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt index 1f5dc73f732..a2ae350ce4b 100644 --- a/source/blender/editors/CMakeLists.txt +++ b/source/blender/editors/CMakeLists.txt @@ -22,6 +22,7 @@ if(WITH_BLENDER) add_subdirectory(animation) add_subdirectory(armature) + add_subdirectory(asset) add_subdirectory(curve) add_subdirectory(geometry) add_subdirectory(gizmo_library) diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 9e622aea6ab..0db3e984b60 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -503,7 +503,7 @@ static void ANIM_OT_previewrange_clear(wmOperatorType *ot) /* identifiers */ ot->name = "Clear Preview Range"; ot->idname = "ANIM_OT_previewrange_clear"; - ot->description = "Clear Preview Range"; + ot->description = "Clear preview range"; /* api callbacks */ ot->exec = previewrange_clear_exec; diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index b20d2738bda..f2cb00f67f0 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -1014,7 +1014,7 @@ void ARMATURE_OT_switch_direction(wmOperatorType *ot) /* identifiers */ ot->name = "Switch Direction"; ot->idname = "ARMATURE_OT_switch_direction"; - ot->description = "Change the direction that a chain of bones points in (head <-> tail swap)"; + ot->description = "Change the direction that a chain of bones points in (head and tail swap)"; /* api callbacks */ ot->exec = armature_switch_direction_exec; diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c index c217b615db6..7c11c5e537e 100644 --- a/source/blender/editors/armature/editarmature_undo.c +++ b/source/blender/editors/armature/editarmature_undo.c @@ -26,7 +26,9 @@ #include "CLG_log.h" #include "DNA_armature_types.h" +#include "DNA_layer_types.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "BLI_array_utils.h" #include "BLI_listbase.h" diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt new file mode 100644 index 00000000000..63a1761b264 --- /dev/null +++ b/source/blender/editors/asset/CMakeLists.txt @@ -0,0 +1,39 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + ../include + ../../blenlib + ../../blenkernel + ../../makesdna + ../../makesrna + ../../windowmanager + ../../../../intern/guardedalloc +) + +set(INC_SYS +) + +set(SRC + asset_edit.c + asset_ops.c +) + +set(LIB +) + +blender_add_lib(bf_editor_asset "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/asset/asset_edit.c b/source/blender/editors/asset/asset_edit.c new file mode 100644 index 00000000000..5333c08c66a --- /dev/null +++ b/source/blender/editors/asset/asset_edit.c @@ -0,0 +1,69 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#include "BKE_asset.h" +#include "BKE_context.h" +#include "BKE_idtype.h" +#include "BKE_lib_id.h" + +#include "DNA_ID.h" +#include "DNA_asset_types.h" + +#include "UI_interface_icons.h" + +#include "RNA_access.h" + +#include "ED_asset.h" + +bool ED_asset_mark_id(const bContext *C, ID *id) +{ + if (id->asset_data) { + return false; + } + if (!BKE_id_can_be_asset(id)) { + return false; + } + + id_fake_user_set(id); + + id->asset_data = BKE_asset_metadata_create(); + + UI_icon_render_id(C, NULL, id, true, true); + + return true; +} + +bool ED_asset_clear_id(ID *id) +{ + if (!id->asset_data) { + return false; + } + BKE_asset_metadata_free(&id->asset_data); + /* Don't clear fake user here, there's no guarantee that it was actually set by + * #ED_asset_mark_id(), it might have been something/someone else. */ + + return true; +} + +bool ED_asset_can_make_single_from_context(const bContext *C) +{ + /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ + return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != NULL; +} diff --git a/source/blender/editors/asset/asset_ops.c b/source/blender/editors/asset/asset_ops.c new file mode 100644 index 00000000000..929d49e19fa --- /dev/null +++ b/source/blender/editors/asset/asset_ops.c @@ -0,0 +1,238 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#include <string.h> + +#include "BKE_asset.h" +#include "BKE_context.h" +#include "BKE_report.h" + +#include "BLI_listbase.h" +#include "BLI_string_utils.h" + +#include "DNA_asset_types.h" + +#include "ED_asset.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* -------------------------------------------------------------------- */ + +struct AssetMarkResultStats { + int tot_created; + int tot_already_asset; + ID *last_id; +}; + +/** + * Return the IDs to operate on as list of #CollectionPointerLink links. Needs freeing. + */ +static ListBase /* CollectionPointerLink */ asset_operation_get_ids_from_context(const bContext *C) +{ + ListBase list = {0}; + + PointerRNA idptr = CTX_data_pointer_get_type(C, "id", &RNA_ID); + + if (idptr.data) { + CollectionPointerLink *ctx_link = MEM_callocN(sizeof(*ctx_link), __func__); + ctx_link->ptr = idptr; + BLI_addtail(&list, ctx_link); + } + else { + CTX_data_selected_ids(C, &list); + } + + return list; +} + +static void asset_mark_for_idptr_list(const bContext *C, + const ListBase /* CollectionPointerLink */ *ids, + struct AssetMarkResultStats *r_stats) +{ + memset(r_stats, 0, sizeof(*r_stats)); + + LISTBASE_FOREACH (CollectionPointerLink *, ctx_id, ids) { + BLI_assert(RNA_struct_is_ID(ctx_id->ptr.type)); + + ID *id = ctx_id->ptr.data; + if (id->asset_data) { + r_stats->tot_already_asset++; + continue; + } + + if (ED_asset_mark_id(C, id)) { + r_stats->last_id = id; + r_stats->tot_created++; + } + } +} + +static bool asset_mark_results_report(const struct AssetMarkResultStats *stats, + ReportList *reports) +{ + /* User feedback on failure. */ + if ((stats->tot_created < 1) && (stats->tot_already_asset > 0)) { + BKE_report(reports, + RPT_ERROR, + "Selected data-blocks are already assets (or do not support use as assets)"); + return false; + } + if (stats->tot_created < 1) { + BKE_report(reports, + RPT_ERROR, + "No data-blocks to create assets for found (or do not support use as assets)"); + return false; + } + + /* User feedback on success. */ + if (stats->tot_created == 1) { + /* If only one data-block: Give more useful message by printing asset name. */ + BKE_reportf(reports, RPT_INFO, "Data-block '%s' is now an asset", stats->last_id->name + 2); + } + else { + BKE_reportf(reports, RPT_INFO, "%i data-blocks are now assets", stats->tot_created); + } + + return true; +} + +static int asset_mark_exec(bContext *C, wmOperator *op) +{ + ListBase ids = asset_operation_get_ids_from_context(C); + + struct AssetMarkResultStats stats; + asset_mark_for_idptr_list(C, &ids, &stats); + BLI_freelistN(&ids); + + if (!asset_mark_results_report(&stats, op->reports)) { + return OPERATOR_CANCELLED; + } + + WM_main_add_notifier(NC_ID | NA_EDITED, NULL); + WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL); + + return OPERATOR_FINISHED; +} + +static void ASSET_OT_mark(wmOperatorType *ot) +{ + ot->name = "Mark Asset"; + ot->description = + "Enable easier reuse of selected data-blocks through the Asset Browser, with the help of " + "customizable metadata (like previews, descriptions and tags)"; + ot->idname = "ASSET_OT_mark"; + + ot->exec = asset_mark_exec; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* -------------------------------------------------------------------- */ + +struct AssetClearResultStats { + int tot_removed; + ID *last_id; +}; + +static void asset_clear_from_idptr_list(const ListBase /* CollectionPointerLink */ *ids, + struct AssetClearResultStats *r_stats) +{ + memset(r_stats, 0, sizeof(*r_stats)); + + LISTBASE_FOREACH (CollectionPointerLink *, ctx_id, ids) { + BLI_assert(RNA_struct_is_ID(ctx_id->ptr.type)); + + ID *id = ctx_id->ptr.data; + if (!id->asset_data) { + continue; + } + + if (ED_asset_clear_id(id)) { + r_stats->tot_removed++; + r_stats->last_id = id; + } + } +} + +static bool asset_clear_result_report(const struct AssetClearResultStats *stats, + ReportList *reports) + +{ + if (stats->tot_removed < 1) { + BKE_report(reports, RPT_ERROR, "No asset data-blocks selected/focused"); + return false; + } + + if (stats->tot_removed == 1) { + /* If only one data-block: Give more useful message by printing asset name. */ + BKE_reportf( + reports, RPT_INFO, "Data-block '%s' is no asset anymore", stats->last_id->name + 2); + } + else { + BKE_reportf(reports, RPT_INFO, "%i data-blocks are no assets anymore", stats->tot_removed); + } + + return true; +} + +static int asset_clear_exec(bContext *C, wmOperator *op) +{ + ListBase ids = asset_operation_get_ids_from_context(C); + + struct AssetClearResultStats stats; + asset_clear_from_idptr_list(&ids, &stats); + BLI_freelistN(&ids); + + if (!asset_clear_result_report(&stats, op->reports)) { + return OPERATOR_CANCELLED; + } + + WM_main_add_notifier(NC_ID | NA_EDITED, NULL); + WM_main_add_notifier(NC_ASSET | NA_REMOVED, NULL); + + return OPERATOR_FINISHED; +} + +static void ASSET_OT_clear(wmOperatorType *ot) +{ + ot->name = "Clear Asset"; + ot->description = + "Delete all asset metadata and turn the selected asset data-blocks back into normal " + "data-blocks"; + ot->idname = "ASSET_OT_clear"; + + ot->exec = asset_clear_exec; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* -------------------------------------------------------------------- */ + +void ED_operatortypes_asset(void) +{ + WM_operatortype_append(ASSET_OT_mark); + WM_operatortype_append(ASSET_OT_clear); +} diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index db472c9ffa7..2b627971286 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -3838,7 +3838,7 @@ void CURVE_OT_subdivide(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of cuts", "", 1, 10); + prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10); /* Avoid re-using last var because it can cause _very_ high poly meshes * and annoy users (or worse crash). */ RNA_def_property_flag(prop, PROP_SKIP_SAVE); diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 93767127cc7..4e2951c3571 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -41,6 +41,7 @@ #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index 315b3c281da..65141442237 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -25,6 +25,7 @@ #include "BLI_utildefines.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index f26fd936d40..0c8cc621a3b 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -25,6 +25,7 @@ #include "BLI_utildefines.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 63aa242275a..09b57029350 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -41,6 +41,7 @@ #include "DNA_collection_types.h" #include "DNA_curve_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 33a1469beab..aff109eb98e 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -42,6 +42,7 @@ #include "DNA_anim_types.h" #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" @@ -1330,19 +1331,24 @@ static int gpencil_merge_layer_exec(bContext *C, wmOperator *op) BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_dst->framenum), gpf_dst); } - /* Read all frames from merge layer and add any missing in destination layer. */ + /* Read all frames from merge layer and add any missing in destination layer, + * copying all previous strokes to keep the image equals. + * Need to do it in a separated loop to avoid strokes accumulation. */ LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { /* Try to find frame in destination layer hash table. */ bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum)); if (!gpf_dst) { - gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum); - /* Duplicate strokes into destination frame. */ - if (gpf_dst) { - BKE_gpencil_frame_copy_strokes(gpf_src, gpf_dst); - } + gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf_src->framenum, GP_GETFRAME_ADD_COPY); + BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum), gpf_dst); } - else { - /* Add to tail all strokes. */ + } + + /* Read all frames from merge layer and add strokes. */ + LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { + /* Try to find frame in destination layer hash table. */ + bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum)); + /* Add to tail all strokes. */ + if (gpf_dst) { BLI_movelisttolist(&gpf_dst->strokes, &gpf_src->strokes); } } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 95c94f8cfed..36e383cf3c2 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -41,6 +41,7 @@ #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 93941ea3766..39968aac9fa 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -35,6 +35,7 @@ #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" #include "DNA_image_types.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_windowmanager_types.h" diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 3617f20763e..9bca294cf30 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -283,8 +283,8 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); tgpil->gpl = gpl; - tgpil->prevFrame = gpl->actframe; - tgpil->nextFrame = gpl->actframe->next; + tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe); + tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next); BLI_addtail(&tgpi->ilayers, tgpil); @@ -326,24 +326,25 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) valid = false; } - /* create new stroke */ - new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); - if (valid) { /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { - new_stroke->points = MEM_recallocN(new_stroke->points, - sizeof(*new_stroke->points) * gps_to->totpoints); - if (new_stroke->dvert != NULL) { - new_stroke->dvert = MEM_recallocN(new_stroke->dvert, - sizeof(*new_stroke->dvert) * gps_to->totpoints); - } - new_stroke->totpoints = gps_to->totpoints; + BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true); } - /* update points position */ + if (gps_to->totpoints > gps_from->totpoints) { + BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true); + } + + /* Create new stroke. */ + new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); + + /* Update points position. */ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor); } else { + /* Create new stroke. */ + new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); + /* need an empty stroke to keep index correct for lookup, but resize to smallest size */ new_stroke->totpoints = 0; new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points)); @@ -443,12 +444,16 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op) /* finally, free memory used by temp data */ LISTBASE_FOREACH (tGPDinterpolate_layer *, tgpil, &tgpi->ilayers) { + BKE_gpencil_free_strokes(tgpil->prevFrame); + BKE_gpencil_free_strokes(tgpil->nextFrame); BKE_gpencil_free_strokes(tgpil->interFrame); - MEM_freeN(tgpil->interFrame); + MEM_SAFE_FREE(tgpil->prevFrame); + MEM_SAFE_FREE(tgpil->nextFrame); + MEM_SAFE_FREE(tgpil->interFrame); } BLI_freelistN(&tgpi->ilayers); - MEM_freeN(tgpi); + MEM_SAFE_FREE(tgpi); } DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); @@ -992,8 +997,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } /* store extremes */ - prevFrame = gpl->actframe; - nextFrame = gpl->actframe->next; + prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe); + nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next); /* Loop over intermediary frames and create the interpolation */ for (cframe = prevFrame->framenum + step; cframe < nextFrame->framenum; cframe += step) { @@ -1049,28 +1054,17 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN; } - /* create new stroke */ - bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); - /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { - /* free weights of removed points */ - if (new_stroke->dvert != NULL) { - BKE_defvert_array_free_elems(new_stroke->dvert + gps_to->totpoints, - gps_from->totpoints - gps_to->totpoints); - } - - new_stroke->points = MEM_recallocN(new_stroke->points, - sizeof(*new_stroke->points) * gps_to->totpoints); - - if (new_stroke->dvert != NULL) { - new_stroke->dvert = MEM_recallocN(new_stroke->dvert, - sizeof(*new_stroke->dvert) * gps_to->totpoints); - } - - new_stroke->totpoints = gps_to->totpoints; + BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true); + } + if (gps_to->totpoints > gps_from->totpoints) { + BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true); } + /* create new stroke */ + bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true); + /* update points position */ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor); @@ -1081,6 +1075,11 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) BLI_addtail(&interFrame->strokes, new_stroke); } } + + BKE_gpencil_free_strokes(prevFrame); + BKE_gpencil_free_strokes(nextFrame); + MEM_SAFE_FREE(prevFrame); + MEM_SAFE_FREE(nextFrame); } /* notifiers */ diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c index 9f2bf3818a4..272dff56291 100644 --- a/source/blender/editors/gpencil/gpencil_merge.c +++ b/source/blender/editors/gpencil/gpencil_merge.c @@ -31,6 +31,7 @@ #include "BLI_math.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "BKE_brush.h" #include "BKE_context.h" diff --git a/source/blender/editors/gpencil/gpencil_ops_versioning.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c index 4721736489e..815bbbaa254 100644 --- a/source/blender/editors/gpencil/gpencil_ops_versioning.c +++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c @@ -33,6 +33,7 @@ #include "BLI_math.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index ed18c2eed5d..bb9dd8cac5d 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -41,6 +41,7 @@ #include "BLT_translation.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 2c0b9534141..281ab8c5adc 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -36,6 +36,7 @@ #include "BLI_utildefines.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index e8e25a55796..c3ac33063af 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -40,7 +40,9 @@ #include "PIL_time.h" #include "DNA_brush_types.h" +#include "DNA_collection_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c index c3fd8d10b64..90b2c1c3895 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_ops.c +++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c @@ -32,6 +32,7 @@ #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "BKE_colortools.h" #include "BKE_context.h" diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c index a4dc677f0dc..3afff897734 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_paint.c +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -31,6 +31,7 @@ #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "BKE_brush.h" #include "BKE_colortools.h" diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 3501acd4fdf..0c4576096fb 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -31,7 +31,6 @@ struct Base; struct Bone; struct Depsgraph; struct EditBone; -struct IDProperty; struct ListBase; struct Main; struct Mesh; diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h new file mode 100644 index 00000000000..6fe50528cc5 --- /dev/null +++ b/source/blender/editors/include/ED_asset.h @@ -0,0 +1,39 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup editors + */ + +#ifndef __ED_ASSET_H__ +#define __ED_ASSET_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +bool ED_asset_mark_id(const struct bContext *C, struct ID *id); +bool ED_asset_clear_id(struct ID *id); + +bool ED_asset_can_make_single_from_context(const struct bContext *C); + +void ED_operatortypes_asset(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ED_ASSET_H__ */ diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index a5a8df916d6..7b240e0569f 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -28,7 +28,9 @@ extern "C" { #endif struct ARegion; +struct FileAssetSelectParams; struct FileSelectParams; +struct FileDirEntry; struct Scene; struct ScrArea; struct SpaceFile; @@ -103,14 +105,14 @@ struct rcti; struct FileSelectParams *ED_fileselect_ensure_active_params(struct SpaceFile *sfile); struct FileSelectParams *ED_fileselect_get_active_params(const struct SpaceFile *sfile); +struct FileSelectParams *ED_fileselect_get_file_params(const struct SpaceFile *sfile); +struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceFile *sfile); void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile); void ED_fileselect_params_to_userdef(struct SpaceFile *sfile, const int temp_win_size[], const bool is_maximized); -void ED_fileselect_reset_params(struct SpaceFile *sfile); - void ED_fileselect_init_layout(struct SpaceFile *sfile, struct ARegion *region); FileLayout *ED_fileselect_get_layout(struct SpaceFile *sfile, struct ARegion *region); @@ -142,6 +144,8 @@ void ED_fileselect_exit(struct wmWindowManager *wm, struct Scene *owner_scene, struct SpaceFile *sfile); +bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile); + void ED_fileselect_window_params_get(const struct wmWindow *win, int win_size[2], bool *is_maximized); @@ -151,6 +155,7 @@ struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win, int ED_path_extension_type(const char *path); int ED_file_extension_icon(const char *path); +int ED_file_icon(const struct FileDirEntry *file); void ED_file_read_bookmarks(void); diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index be2f714dfe1..1b7caf27ecf 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -51,7 +51,6 @@ struct ScrArea; struct SnapObjectContext; struct ToolSettings; struct View3D; -struct ViewLayer; struct bContext; struct Material; diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index c1d3a17b9b6..b139b0765a3 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -34,12 +34,10 @@ struct ARegion; struct ImBuf; struct Image; struct ImageUser; -struct LinkNodePair; struct Main; struct ReportList; struct Scene; struct SpaceImage; -struct ViewLayer; struct bContext; struct wmOperator; struct wmWindowManager; diff --git a/source/blender/editors/include/ED_info.h b/source/blender/editors/include/ED_info.h index e3ce494e09a..9ac6b6c1085 100644 --- a/source/blender/editors/include/ED_info.h +++ b/source/blender/editors/include/ED_info.h @@ -38,8 +38,12 @@ const char *ED_info_statistics_string(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer); -void ED_info_draw_stats( - struct Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height); +void ED_info_draw_stats(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer, + int x, + int *y, + int height); #ifdef __cplusplus } diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 2e9b711c99a..f9358f62274 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -53,7 +53,6 @@ struct uiLayout; struct wmKeyConfig; struct wmOperator; struct wmOperatorType; -struct wmWindowManager; /* object_edit.c */ /* context.object */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index dc1c43c0337..20417634020 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -239,6 +239,7 @@ void ED_screen_restore_temp_type(struct bContext *C, ScrArea *area); ScrArea *ED_screen_full_newspace(struct bContext *C, ScrArea *area, int type); void ED_screen_full_prevspace(struct bContext *C, ScrArea *area); void ED_screen_full_restore(struct bContext *C, ScrArea *area); +ScrArea *ED_screen_state_maximized_create(struct bContext *C); struct ScrArea *ED_screen_state_toggle(struct bContext *C, struct wmWindow *win, struct ScrArea *area, diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 0ea86e006e0..ca3e351a052 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -32,7 +32,6 @@ extern "C" { struct Object; struct bContext; struct wmKeyConfig; -struct wmMsgBus; struct wmOperatorType; void ED_keymap_transform(struct wmKeyConfig *keyconf); @@ -108,7 +107,6 @@ bool calculateTransformCenter(struct bContext *C, struct Object; struct Scene; -struct wmGizmoGroup; struct wmGizmoGroupType; /* UNUSED */ diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index ebaa32941f2..b7174964ef6 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -31,7 +31,6 @@ struct BMVert; struct ARegion; struct Depsgraph; struct ListBase; -struct Main; struct Object; struct Scene; struct View3D; diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 68ae3589064..ca6b4bdc618 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -53,6 +53,8 @@ void ED_spacedata_id_remap(struct ScrArea *area, struct ID *new_id); void ED_OT_flush_edits(struct wmOperatorType *ot); +void ED_OT_lib_id_load_custom_preview(struct wmOperatorType *ot); +void ED_OT_lib_id_generate_preview(struct wmOperatorType *ot); /* ************** XXX OLD CRUFT WARNING ************* */ diff --git a/source/blender/editors/include/ED_util_imbuf.h b/source/blender/editors/include/ED_util_imbuf.h index d142d3d6425..4bbaa68e849 100644 --- a/source/blender/editors/include/ED_util_imbuf.h +++ b/source/blender/editors/include/ED_util_imbuf.h @@ -31,7 +31,6 @@ extern "C" { #endif struct ARegion; -struct Main; struct bContext; struct wmEvent; struct wmOperator; diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 2066d7da511..4de97411059 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -33,7 +33,6 @@ struct BMEditMesh; struct BMFace; struct BMLoop; struct BMesh; -struct Depsgraph; struct Image; struct ImageUser; struct Main; @@ -217,8 +216,10 @@ struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm); void ED_uvedit_active_edge_loop_set(struct BMesh *bm, struct BMLoop *l); struct BMLoop *ED_uvedit_active_edge_loop_get(struct BMesh *bm); -char ED_uvedit_select_mode_get(const Scene *scene); -void ED_uvedit_select_sync_flush(const ToolSettings *ts, struct BMEditMesh *em, const bool select); +char ED_uvedit_select_mode_get(const struct Scene *scene); +void ED_uvedit_select_sync_flush(const struct ToolSettings *ts, + struct BMEditMesh *em, + const bool select); /* uvedit_unwrap_ops.c */ void ED_uvedit_live_unwrap_begin(struct Scene *scene, struct Object *obedit); @@ -244,7 +245,7 @@ struct UVPackIsland_Params { uint use_seams : 1; uint correct_aspect : 1; }; -void ED_uvedit_pack_islands_multi(const Scene *scene, +void ED_uvedit_pack_islands_multi(const struct Scene *scene, Object **objects, const uint objects_len, const struct UVPackIsland_Params *params); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 596533406c3..a4856845a65 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -41,8 +41,6 @@ struct Camera; struct CustomData_MeshMasks; struct Depsgraph; struct EditBone; -struct GPUOffScreen; -struct GPUViewport; struct ID; struct MVert; struct Main; @@ -55,7 +53,6 @@ struct RenderEngineType; struct Scene; struct ScrArea; struct View3D; -struct View3DShading; struct ViewContext; struct ViewLayer; struct bContext; @@ -64,8 +61,6 @@ struct bScreen; struct rctf; struct rcti; struct wmGizmo; -struct wmOperator; -struct wmOperatorType; struct wmWindow; struct wmWindowManager; @@ -141,6 +136,11 @@ void ED_view3d_to_object(const struct Depsgraph *depsgraph, const float quat[4], const float dist); +bool ED_view3d_camera_to_view_selected(struct Main *bmain, + struct Depsgraph *depsgraph, + const struct Scene *scene, + struct Object *camera_ob); + void ED_view3d_lastview_store(struct RegionView3D *rv3d); /* Depth buffer */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 005dbf0e381..7c128cbf1e6 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -50,7 +50,6 @@ struct PointerRNA; struct PropertyRNA; struct ReportList; struct ResultBLF; -struct ScrArea; struct bContext; struct bContextStore; struct bNode; @@ -727,6 +726,13 @@ void UI_block_translate(uiBlock *block, int x, int y); int UI_but_return_value_get(uiBut *but); void UI_but_drag_set_id(uiBut *but, struct ID *id); +void UI_but_drag_set_asset(uiBut *but, + const char *name, + const char *path, + int id_type, + int icon, + struct ImBuf *imb, + float scale); void UI_but_drag_set_rna(uiBut *but, struct PointerRNA *ptr); void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free); void UI_but_drag_set_name(uiBut *but, const char *name); @@ -1879,6 +1885,7 @@ uiBlock *uiLayoutGetBlock(uiLayout *layout); void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv); void uiLayoutSetContextPointer(uiLayout *layout, const char *name, struct PointerRNA *ptr); +struct bContextStore *uiLayoutGetContextStore(uiLayout *layout); void uiLayoutContextCopy(uiLayout *layout, struct bContextStore *context); struct wmOperatorType *UI_but_operatortype_get_from_enum_menu(struct uiBut *but, PropertyRNA **r_prop); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 4a02c6b6e88..c5c2f0e55c4 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -3352,7 +3352,7 @@ static void ui_but_free(const bContext *C, uiBut *but) } if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) { - MEM_freeN(but->dragpoin); + WM_drag_data_free(but->dragtype, but->dragpoin); } ui_but_extra_operator_icons_free(but); @@ -4552,6 +4552,15 @@ static uiBut *ui_def_but_rna(uiBlock *block, UI_but_disable(but, info); } + if (proptype == PROP_POINTER) { + /* If the button shows an ID, automatically set it as focused in context so operators can + * access it.*/ + const PointerRNA pptr = RNA_property_pointer_get(ptr, prop); + if (pptr.data && RNA_struct_is_ID(pptr.type)) { + but->context = CTX_store_add(&block->contexts, "id", &pptr); + } + } + if (but->flag & UI_BUT_UNDO && (ui_but_is_rna_undo(but) == false)) { but->flag &= ~UI_BUT_UNDO; } @@ -6089,17 +6098,42 @@ void UI_but_drag_set_id(uiBut *but, ID *id) { but->dragtype = WM_DRAG_ID; if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { - MEM_SAFE_FREE(but->dragpoin); + WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } but->dragpoin = (void *)id; } +void UI_but_drag_set_asset(uiBut *but, + const char *name, + const char *path, + int id_type, + int icon, + struct ImBuf *imb, + float scale) +{ + wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset"); + + BLI_strncpy(asset_drag->name, name, sizeof(asset_drag->name)); + asset_drag->path = path; + asset_drag->id_type = id_type; + + but->dragtype = WM_DRAG_ASSET; + ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ + if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + WM_drag_data_free(but->dragtype, but->dragpoin); + } + but->dragpoin = asset_drag; + but->dragflag |= UI_BUT_DRAGPOIN_FREE; + but->imb = imb; + but->imb_scale = scale; +} + void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr) { but->dragtype = WM_DRAG_RNA; if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { - MEM_SAFE_FREE(but->dragpoin); + WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } but->dragpoin = (void *)ptr; @@ -6109,7 +6143,7 @@ void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free) { but->dragtype = WM_DRAG_PATH; if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { - MEM_SAFE_FREE(but->dragpoin); + WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } but->dragpoin = (void *)path; @@ -6122,7 +6156,7 @@ void UI_but_drag_set_name(uiBut *but, const char *name) { but->dragtype = WM_DRAG_NAME; if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { - MEM_SAFE_FREE(but->dragpoin); + WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } but->dragpoin = (void *)name; @@ -6140,7 +6174,7 @@ void UI_but_drag_set_image( but->dragtype = WM_DRAG_PATH; ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { - MEM_SAFE_FREE(but->dragpoin); + WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } but->dragpoin = (void *)path; diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 39b405a02b8..870c3a2a13f 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -38,6 +38,7 @@ #include "BKE_idprop.h" #include "BKE_screen.h" +#include "ED_asset.h" #include "ED_keyframing.h" #include "ED_screen.h" @@ -503,6 +504,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) uiPopupMenu *pup; uiLayout *layout; + bContextStore *previous_ctx = CTX_store_get(C); { uiStringInfo label = {BUT_GET_LABEL, NULL}; @@ -514,6 +516,11 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) if (label.strinfo) { MEM_freeN(label.strinfo); } + + if (but->context) { + uiLayoutContextCopy(layout, but->context); + CTX_store_set(C, uiLayoutGetContextStore(layout)); + } uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); } @@ -946,6 +953,22 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) } } + /* If the button reprents an id, it can set the "id" context pointer. */ + if (ED_asset_can_make_single_from_context(C)) { + ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data; + + /* Gray out items depending on if data-block is an asset. Preferably this could be done via + * operator poll, but that doesn't work since the operator also works with "selected_ids", + * which isn't cheap to check. */ + uiLayout *sub = uiLayoutColumn(layout, true); + uiLayoutSetEnabled(sub, !id->asset_data); + uiItemO(sub, NULL, ICON_NONE, "ASSET_OT_mark"); + sub = uiLayoutColumn(layout, true); + uiLayoutSetEnabled(sub, id->asset_data); + uiItemO(sub, NULL, ICON_NONE, "ASSET_OT_clear"); + uiItemS(layout); + } + /* Pointer properties and string properties with * prop_search support jumping to target object/bone. */ if (but->rnapoin.data && but->rnaprop) { @@ -1210,6 +1233,10 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) UI_menutype_draw(C, mt, uiLayoutColumn(layout, false)); } + if (but->context) { + CTX_store_set(C, previous_ctx); + } + return UI_popup_menu_end_or_cancel(C, pup); } diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c index 7f735a0e789..f2899fc0098 100644 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c @@ -34,6 +34,7 @@ #include "BLT_translation.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_space_types.h" #include "BKE_context.h" diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index f914ccd7497..72ed2cc0933 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -626,7 +626,11 @@ static bool ui_rna_is_userdef(PointerRNA *ptr, PropertyRNA *prop) if (base == NULL) { base = ptr->type; } - if (ELEM(base, &RNA_AddonPreferences, &RNA_KeyConfigPreferences, &RNA_KeyMapItem)) { + if (ELEM(base, + &RNA_AddonPreferences, + &RNA_KeyConfigPreferences, + &RNA_KeyMapItem, + &RNA_UserAssetLibrary)) { tag = true; } } @@ -1349,6 +1353,9 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl if (mbut_state == NULL) { /* Highly unlikely. */ printf("%s: Can't find button\n", __func__); + /* While this avoids crashing, multi-button dragging will fail, + * which is still a bug from the user perspective. See T83651. */ + continue; } void *active_back; @@ -1984,6 +1991,8 @@ static bool ui_but_drag_init(bContext *C, else { wmDrag *drag = WM_event_start_drag( C, but->icon, but->dragtype, but->dragpoin, ui_but_value_get(but), WM_DRAG_NOP); + /* wmDrag has ownership over dragpoin now, stop messing with it. */ + but->dragpoin = NULL; if (but->imb) { WM_event_drag_image(drag, @@ -2256,10 +2265,11 @@ static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleB ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */ LISTBASE_FOREACH (wmDrag *, wmd, drags) { + /* TODO asset dropping. */ if (wmd->type == WM_DRAG_ID) { /* align these types with UI_but_active_drop_name */ if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - ID *id = WM_drag_ID(wmd, 0); + ID *id = WM_drag_get_local_ID(wmd, 0); button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 90f5172f6ec..899f4a6ddb1 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -100,11 +100,12 @@ typedef void (*VectorDrawFunc)(int x, int y, int w, int h, float alpha); #define ICON_TYPE_COLOR_TEXTURE 1 #define ICON_TYPE_MONO_TEXTURE 2 #define ICON_TYPE_BUFFER 3 -#define ICON_TYPE_VECTOR 4 -#define ICON_TYPE_GEOM 5 -#define ICON_TYPE_EVENT 6 /* draw keymap entries using custom renderer. */ -#define ICON_TYPE_GPLAYER 7 -#define ICON_TYPE_BLANK 8 +#define ICON_TYPE_IMBUF 4 +#define ICON_TYPE_VECTOR 5 +#define ICON_TYPE_GEOM 6 +#define ICON_TYPE_EVENT 7 /* draw keymap entries using custom renderer. */ +#define ICON_TYPE_GPLAYER 8 +#define ICON_TYPE_BLANK 9 typedef struct DrawInfo { int type; @@ -1147,6 +1148,9 @@ static DrawInfo *icon_create_drawinfo(Icon *icon) if (ELEM(icon_data_type, ICON_DATA_ID, ICON_DATA_PREVIEW)) { di->type = ICON_TYPE_PREVIEW; } + else if (icon_data_type == ICON_DATA_IMBUF) { + di->type = ICON_TYPE_IMBUF; + } else if (icon_data_type == ICON_DATA_GEOM) { di->type = ICON_TYPE_GEOM; } @@ -1262,7 +1266,7 @@ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size) else if (!prv_img->rect[size]) { prv_img->w[size] = render_size; prv_img->h[size] = render_size; - prv_img->flag[size] |= PRV_CHANGED; + prv_img->flag[size] |= (PRV_CHANGED | PRV_UNFINISHED); prv_img->changed_timestamp[size] = 0; prv_img->rect[size] = MEM_callocN(render_size * render_size * sizeof(uint), "prv_rect"); } @@ -1384,8 +1388,12 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi } } -/* only called when icon has changed */ -/* only call with valid pointer from UI_icon_draw */ +/** + * * Only call with valid pointer from UI_icon_draw. + * * Only called when icon has changed. + * + * Note that if an ID doesn't support jobs for preview creation, \a use_job will be ignored. + */ static void icon_set_image(const bContext *C, Scene *scene, ID *id, @@ -1408,7 +1416,7 @@ static void icon_set_image(const bContext *C, const bool delay = prv_img->rect[size] != NULL; icon_create_rect(prv_img, size); - if (use_job) { + if (use_job && (!id || BKE_previewimg_id_supports_jobs(id))) { /* Job (background) version */ ED_preview_icon_job( C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size], delay); @@ -1790,7 +1798,14 @@ static void icon_draw_size(float x, /* We need to flush widget base first to ensure correct ordering. */ UI_widgetbase_draw_cache_flush(); - if (di->type == ICON_TYPE_VECTOR) { + if (di->type == ICON_TYPE_IMBUF) { + ImBuf *ibuf = icon->obj; + + GPU_blend(GPU_BLEND_ALPHA_PREMULT); + icon_draw_rect(x, y, w, h, aspect, ibuf->x, ibuf->y, ibuf->rect, alpha, desaturate); + GPU_blend(GPU_BLEND_ALPHA); + } + else if (di->type == ICON_TYPE_VECTOR) { /* vector icons use the uiBlock transformation, they are not drawn * with untransformed coordinates like the other icons */ di->data.vector.func((int)x, (int)y, w, h, 1.0f); @@ -1937,6 +1952,9 @@ static void ui_id_preview_image_render_size( } } +/** + * Note that if an ID doesn't support jobs for preview creation, \a use_job will be ignored. + */ void UI_icon_render_id(const bContext *C, Scene *scene, ID *id, const bool big, const bool use_job) { PreviewImage *pi = BKE_previewimg_id_ensure(id); @@ -1964,12 +1982,7 @@ static void ui_id_icon_render(const bContext *C, ID *id, bool use_jobs) } for (enum eIconSizes i = 0; i < NUM_ICON_SIZES; i++) { - /* check if rect needs to be created; changed - * only set by dynamic icons */ - if (((pi->flag[i] & PRV_CHANGED) || !pi->rect[i])) { - icon_set_image(C, NULL, id, pi, i, use_jobs); - pi->flag[i] &= ~PRV_CHANGED; - } + ui_id_preview_image_render_size(C, NULL, id, pi, i, use_jobs); } } @@ -2186,6 +2199,9 @@ int UI_icon_from_library(const ID *id) if (ID_IS_OVERRIDE_LIBRARY(id)) { return ICON_LIBRARY_DATA_OVERRIDE; } + if (ID_IS_ASSET(id)) { + return ICON_MAT_SPHERE_SKY; + } return ICON_NONE; } diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 0403287125c..4b19b8f97f4 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -651,7 +651,7 @@ static void ui_item_array(uiLayout *layout, uiDefAutoButR(block, ptr, prop, -1, "", ICON_NONE, 0, 0, w, UI_UNIT_Y); } else { - /* even if 'expand' is fale, expanding anyway */ + /* Even if 'expand' is false, we expand anyway. */ /* layout for known array subtypes */ char str[3] = {'\0'}; @@ -5663,6 +5663,11 @@ void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *p layout->context = CTX_store_add(&block->contexts, name, ptr); } +bContextStore *uiLayoutGetContextStore(uiLayout *layout) +{ + return layout->context; +} + void uiLayoutContextCopy(uiLayout *layout, bContextStore *context) { uiBlock *block = layout->root->block; diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index c3d528ad5c5..a37fb0dfde1 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -206,8 +206,12 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs, BLF_disable(fs->uifont_id, font_flag); - *r_xofs = xofs; - *r_yofs = yofs; + if (r_xofs) { + *r_xofs = xofs; + } + if (r_yofs) { + *r_yofs = yofs; + } } void UI_fontstyle_draw(const uiFontStyle *fs, diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 43ead511cfe..b895f1702f4 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -895,6 +895,9 @@ static void template_ID(const bContext *C, idfrom = template_ui->ptr.owner_id; // lb = template_ui->idlb; + /* Allow opertators to take the ID from context. */ + uiLayoutSetContextPointer(layout, "id", &idptr); + block = uiLayoutGetBlock(layout); UI_block_align_begin(block); @@ -2011,7 +2014,7 @@ static void set_constraint_expand_flag(const bContext *UNUSED(C), Panel *panel, /** * Function with void * argument for #uiListPanelIDFromDataFunc. * - * \note: Constraint panel types are assumed to be named with the struct name field + * \note Constraint panel types are assumed to be named with the struct name field * concatenated to the defined prefix. */ static void object_constraint_panel_id(void *md_link, char *r_name) @@ -6170,6 +6173,10 @@ void uiTemplateList(uiLayout *layout, org_i, flt_flag); + /* Items should be able to set context pointers for the layout. But the list-row button + * swallows events, so it needs the context storage too for handlers to see it. */ + but->context = uiLayoutGetContextStore(sub); + /* If we are "drawing" active item, set all labels as active. */ if (i == activei) { ui_layout_list_set_labels_active(sub); diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index a5999962e09..7de0ec0255f 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -109,14 +109,13 @@ typedef struct v2dViewPanData { static bool view_pan_poll(bContext *C) { ARegion *region = CTX_wm_region(C); - View2D *v2d; /* check if there's a region in context to work with */ if (region == NULL) { return false; } - v2d = ®ion->v2d; + View2D *v2d = ®ion->v2d; /* check that 2d-view can pan */ if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y)) { @@ -191,10 +190,7 @@ static void view_pan_apply(bContext *C, wmOperator *op) /* cleanup temp customdata */ static void view_pan_exit(wmOperator *op) { - if (op->customdata) { - MEM_freeN(op->customdata); - op->customdata = NULL; - } + MEM_SAFE_FREE(op->customdata); } /** \} */ @@ -216,14 +212,12 @@ static int view_pan_exec(bContext *C, wmOperator *op) static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *window = CTX_wm_window(C); - v2dViewPanData *vpd; - View2D *v2d; /* set up customdata */ view_pan_init(C, op); - vpd = op->customdata; - v2d = vpd->v2d; + v2dViewPanData *vpd = op->customdata; + View2D *v2d = vpd->v2d; /* set initial settings */ vpd->startx = vpd->lastx = event->x; @@ -787,7 +781,6 @@ static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2]) static bool view_zoom_poll(bContext *C) { ARegion *region = CTX_wm_region(C); - View2D *v2d; /* check if there's a region in context to work with */ if (region == NULL) { @@ -799,7 +792,7 @@ static bool view_zoom_poll(bContext *C) return false; } - v2d = ®ion->v2d; + View2D *v2d = ®ion->v2d; /* check that 2d-view is zoomable */ if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y)) { @@ -836,12 +829,12 @@ static void view_zoomstep_apply_ex(bContext *C, ARegion *region = CTX_wm_region(C); View2D *v2d = ®ion->v2d; const rctf cur_old = v2d->cur; - float dx, dy; const int snap_test = ED_region_snap_size_test(region); /* calculate amount to move view by, ensuring symmetry so the * old zoom level is restored after zooming back the same amount */ + float dx, dy; if (facx >= 0.0f) { dx = BLI_rctf_size_x(&v2d->cur) * facx; dy = BLI_rctf_size_y(&v2d->cur) * facy; @@ -955,10 +948,7 @@ static void view_zoomstep_exit(wmOperator *op) { UI_view2d_zoom_cache_reset(); - if (op->customdata) { - MEM_freeN(op->customdata); - op->customdata = NULL; - } + MEM_SAFE_FREE(op->customdata); } /* this operator only needs this single callback, where it calls the view_zoom_*() methods */ @@ -1107,15 +1097,14 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) { v2dViewZoomData *vzd = op->customdata; View2D *v2d = vzd->v2d; - float dx, dy; const int snap_test = ED_region_snap_size_test(vzd->region); const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); const bool zoom_to_pos = use_cursor_init && vzd->zoom_to_mouse_pos; /* get amount to move view by */ - dx = RNA_float_get(op->ptr, "deltax") / U.dpi_fac; - dy = RNA_float_get(op->ptr, "deltay") / U.dpi_fac; + float dx = RNA_float_get(op->ptr, "deltax") / U.dpi_fac; + float dy = RNA_float_get(op->ptr, "deltay") / U.dpi_fac; if (U.uiflag & USER_ZOOM_INVERT) { dx *= -1; @@ -1223,14 +1212,12 @@ static int view_zoomdrag_exec(bContext *C, wmOperator *op) static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *window = CTX_wm_window(C); - v2dViewZoomData *vzd; - View2D *v2d; /* set up customdata */ view_zoomdrag_init(C, op); - vzd = op->customdata; - v2d = vzd->v2d; + v2dViewZoomData *vzd = op->customdata; + View2D *v2d = vzd->v2d; if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) { ARegion *region = CTX_wm_region(C); @@ -1242,20 +1229,18 @@ static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *even } if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) { - float dx, dy, fac; - vzd->lastx = event->prevx; vzd->lasty = event->prevy; /* As we have only 1D information (magnify value), feed both axes * with magnify information that is stored in x axis */ - fac = 0.01f * (event->prevx - event->x); - dx = fac * BLI_rctf_size_x(&v2d->cur) / 10.0f; + float fac = 0.01f * (event->prevx - event->x); + float dx = fac * BLI_rctf_size_x(&v2d->cur) / 10.0f; if (event->type == MOUSEPAN) { fac = 0.01f * (event->prevy - event->y); } - dy = fac * BLI_rctf_size_y(&v2d->cur) / 10.0f; + float dy = fac * BLI_rctf_size_y(&v2d->cur) / 10.0f; /* support trackpad zoom to always zoom entirely - the v2d code uses portrait or * landscape exceptions */ @@ -1491,11 +1476,11 @@ static int view_borderzoom_exec(bContext *C, wmOperator *op) { ARegion *region = CTX_wm_region(C); View2D *v2d = ®ion->v2d; - rctf rect; rctf cur_new = v2d->cur; const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); /* convert coordinates of rect to 'tot' rect coordinates */ + rctf rect; WM_operator_properties_border_to_rctf(op, &rect); UI_view2d_region_to_view_rctf(v2d, &rect, &rect); @@ -1766,13 +1751,13 @@ static int view2d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w ARegion *region = CTX_wm_region(C); View2D *v2d = ®ion->v2d; struct SmoothView2DStore *sms = v2d->sms; - float step; /* escape if not our timer */ if (v2d->smooth_timer == NULL || v2d->smooth_timer != event->customdata) { return OPERATOR_PASS_THROUGH; } + float step; if (sms->time_allowed != 0.0) { step = (float)((v2d->smooth_timer->duration) / sms->time_allowed); } @@ -1978,15 +1963,11 @@ static void scroller_activate_init(bContext *C, const wmEvent *event, const char in_scroller) { - v2dScrollerMove *vsm; - View2DScrollers scrollers; ARegion *region = CTX_wm_region(C); View2D *v2d = ®ion->v2d; - rctf tot_cur_union; - float mask_size; /* set custom-data for operator */ - vsm = MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove"); + v2dScrollerMove *vsm = MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove"); op->customdata = vsm; /* set general data */ @@ -2000,16 +1981,17 @@ static void scroller_activate_init(bContext *C, /* 'zone' depends on where mouse is relative to bubble * - zooming must be allowed on this axis, otherwise, default to pan */ + View2DScrollers scrollers; UI_view2d_scrollers_calc(v2d, NULL, &scrollers); /* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases * moving the scroll bars has far too little effect and the view can get stuck T31476. */ - tot_cur_union = v2d->tot; + rctf tot_cur_union = v2d->tot; BLI_rctf_union(&tot_cur_union, &v2d->cur); if (in_scroller == 'h') { /* horizontal scroller - calculate adjustment factor first */ - mask_size = (float)BLI_rcti_size_x(&v2d->hor); + float mask_size = (float)BLI_rcti_size_x(&v2d->hor); vsm->fac = BLI_rctf_size_x(&tot_cur_union) / mask_size; /* pixel rounding */ @@ -2029,7 +2011,7 @@ static void scroller_activate_init(bContext *C, } else { /* vertical scroller - calculate adjustment factor first */ - mask_size = (float)BLI_rcti_size_y(&v2d->vert); + float mask_size = (float)BLI_rcti_size_y(&v2d->vert); vsm->fac = BLI_rctf_size_y(&tot_cur_union) / mask_size; /* pixel rounding */ @@ -2076,10 +2058,9 @@ static void scroller_activate_apply(bContext *C, wmOperator *op) { v2dScrollerMove *vsm = op->customdata; View2D *v2d = vsm->v2d; - float temp; /* calculate amount to move view by */ - temp = vsm->fac * vsm->delta; + float temp = vsm->fac * vsm->delta; /* round to pixel */ temp = roundf(temp / vsm->fac_round) * vsm->fac_round; @@ -2219,11 +2200,9 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent * /* if in a scroller, init customdata then set modal handler which will * catch mouse-down to start doing useful stuff */ if (in_scroller) { - v2dScrollerMove *vsm; - /* initialize customdata */ scroller_activate_init(C, op, event, in_scroller); - vsm = (v2dScrollerMove *)op->customdata; + v2dScrollerMove *vsm = (v2dScrollerMove *)op->customdata; /* support for quick jump to location - gtk and qt do this on linux */ if (event->type == MIDDLEMOUSE) { @@ -2324,12 +2303,11 @@ static int reset_exec(bContext *C, wmOperator *UNUSED(op)) const uiStyle *style = UI_style_get(); ARegion *region = CTX_wm_region(C); View2D *v2d = ®ion->v2d; - int winx, winy; const int snap_test = ED_region_snap_size_test(region); /* zoom 1.0 */ - winx = (float)(BLI_rcti_size_x(&v2d->mask) + 1); - winy = (float)(BLI_rcti_size_y(&v2d->mask) + 1); + const int winx = (float)(BLI_rcti_size_x(&v2d->mask) + 1); + const int winy = (float)(BLI_rcti_size_y(&v2d->mask) + 1); v2d->cur.xmax = v2d->cur.xmin + winx; v2d->cur.ymax = v2d->cur.ymin + winy; diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index b8badd207fe..e788b28d3b4 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -1129,7 +1129,7 @@ void MESH_OT_bevel(wmOperatorType *ot) prop_affect_items, BEVEL_AFFECT_EDGES, "Affect", - "Affect Edges or Vertices"); + "Affect edges or vertices"); RNA_def_boolean(ot->srna, "clamp_overlap", diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index ef78d31a6bb..aa144dd3f3c 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -177,6 +177,6 @@ void MESH_OT_knife_project(wmOperatorType *ot) RNA_def_boolean(ot->srna, "cut_through", false, - "Cut through", + "Cut Through", "Cut through all faces, not just visible ones"); } diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index a2fe949b6c5..d3eaa9048d3 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -4552,7 +4552,8 @@ void MESH_OT_select_non_manifold(wmOperatorType *ot) /* edges */ RNA_def_boolean(ot->srna, "use_wire", true, "Wire", "Wire edges"); RNA_def_boolean(ot->srna, "use_boundary", true, "Boundaries", "Boundary edges"); - RNA_def_boolean(ot->srna, "use_multi_face", true, "Multiple Faces", "Edges shared by 3+ faces"); + RNA_def_boolean( + ot->srna, "use_multi_face", true, "Multiple Faces", "Edges shared by more than two faces"); RNA_def_boolean(ot->srna, "use_non_contiguous", true, diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c index 00349983c57..d762eede079 100644 --- a/source/blender/editors/mesh/editmesh_select_similar.c +++ b/source/blender/editors/mesh/editmesh_select_similar.c @@ -34,6 +34,7 @@ #include "BKE_material.h" #include "BKE_report.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "WM_api.h" diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index b961f81e16a..cf01170dd8a 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -180,7 +180,7 @@ void MESH_OT_subdivide(wmOperatorType *ot) "ngon", true, "Create N-Gons", - "When disabled, newly created faces are limited to 3-4 sided faces"); + "When disabled, newly created faces are limited to 3 and 4 sided faces"); RNA_def_enum( ot->srna, "quadcorner", @@ -395,7 +395,7 @@ void MESH_OT_unsubdivide(wmOperatorType *ot) { /* identifiers */ ot->name = "Un-Subdivide"; - ot->description = "UnSubdivide selected edges & faces"; + ot->description = "Un-subdivide selected edges and faces"; ot->idname = "MESH_OT_unsubdivide"; /* api callbacks */ @@ -698,7 +698,7 @@ void MESH_OT_edge_collapse(wmOperatorType *ot) /* identifiers */ ot->name = "Collapse Edges & Faces"; ot->description = - "Collapse isolated edges & faces regions, merging data such as UV's and vertex colors. " + "Collapse isolated edge and face regions, merging data such as UV's and vertex colors. " "This can collapse edge-rings as well as regions of connected faces into vertices"; ot->idname = "MESH_OT_edge_collapse"; @@ -1904,7 +1904,7 @@ void MESH_OT_edge_split(wmOperatorType *ot) "VERT", 0, "Faces & Edges by Vertices", - "Split faces & edges connected to selected vertices"}, + "Split faces and edges connected to selected vertices"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 43cad2db185..cff5414da75 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -27,6 +27,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "BLI_array_utils.h" #include "BLI_listbase.h" diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 6467df0e87b..94f386e08d5 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -1139,8 +1139,10 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, if (use_topology) { v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i); - if (respecthide && BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) { - v_mirr = NULL; + if (v_mirr != NULL) { + if (respecthide && BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) { + v_mirr = NULL; + } } } else { diff --git a/source/blender/editors/metaball/editmball_undo.c b/source/blender/editors/metaball/editmball_undo.c index 552e459acb1..b4030ad269b 100644 --- a/source/blender/editors/metaball/editmball_undo.c +++ b/source/blender/editors/metaball/editmball_undo.c @@ -30,8 +30,10 @@ #include "BLI_utildefines.h" #include "DNA_defs.h" +#include "DNA_layer_types.h" #include "DNA_meta_types.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "BKE_context.h" #include "BKE_layer.h" diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index a1be9b9df61..77b5379ddd4 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -31,8 +31,8 @@ set(INC ../../makesrna ../../modifiers ../../python - ../../shader_fx ../../render + ../../shader_fx ../../windowmanager ../../../../intern/clog ../../../../intern/glew-mx @@ -88,6 +88,7 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_GEOMETRY_NODES) + add_definitions(-DWITH_POINT_CLOUD) add_definitions(-DWITH_HAIR_NODES) endif() diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index bbfdfb2532d..a64033bc63a 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1714,6 +1714,9 @@ void OBJECT_OT_hair_add(wmOperatorType *ot) static bool object_pointcloud_add_poll(bContext *C) { + if (!U.experimental.use_new_point_cloud_type) { + return false; + } return ED_operator_objectmode(C); } @@ -2316,17 +2319,23 @@ static const EnumPropertyItem convert_target_items[] = { "MESH", ICON_OUTLINER_OB_MESH, "Mesh", +#ifdef WITH_POINT_CLOUD "Mesh from Curve, Surface, Metaball, Text, or Pointcloud objects"}, +#else + "Mesh from Curve, Surface, Metaball, or Text objects"}, +#endif {OB_GPENCIL, "GPENCIL", ICON_OUTLINER_OB_GREASEPENCIL, "Grease Pencil", "Grease Pencil from Curve or Mesh objects"}, +#ifdef WITH_POINT_CLOUD {OB_POINTCLOUD, "POINTCLOUD", ICON_OUTLINER_OB_POINTCLOUD, "Pointcloud", "Pointcloud from Mesh objects"}, +#endif {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index 008498a1735..9618774eea8 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -25,6 +25,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c index af95f5581bd..e5feb74df26 100644 --- a/source/blender/editors/object/object_gpencil_modifier.c +++ b/source/blender/editors/object/object_gpencil_modifier.c @@ -461,7 +461,7 @@ static bool gpencil_edit_modifier_poll(bContext *C) * (not only from added 'local' ones). */ static bool gpencil_edit_modifier_liboverride_allowed_poll(bContext *C) { - return gpencil_edit_modifier_poll_generic(C, &RNA_Modifier, 0, true); + return gpencil_edit_modifier_poll_generic(C, &RNA_GpencilModifier, 0, true); } static void gpencil_edit_modifier_properties(wmOperatorType *ot) @@ -789,7 +789,7 @@ void OBJECT_OT_gpencil_modifier_apply(wmOperatorType *ot) "apply_as", gpencil_modifier_apply_as_items, MODIFIER_APPLY_DATA, - "Apply as", + "Apply As", "How to apply the modifier to the geometry"); gpencil_edit_modifier_properties(ot); gpencil_edit_modifier_report_property(ot); diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index e6ef53a3d65..89ade5cc49d 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -206,6 +206,7 @@ void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot); /* object_shader_fx.c */ void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot); +void OBJECT_OT_shaderfx_copy(struct wmOperatorType *ot); void OBJECT_OT_shaderfx_remove(struct wmOperatorType *ot); void OBJECT_OT_shaderfx_move_up(struct wmOperatorType *ot); void OBJECT_OT_shaderfx_move_down(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 8ba0ce5fd08..2e5a75ffa7d 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -166,6 +166,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_shaderfx_move_up); WM_operatortype_append(OBJECT_OT_shaderfx_move_down); WM_operatortype_append(OBJECT_OT_shaderfx_move_to_index); + WM_operatortype_append(OBJECT_OT_shaderfx_copy); WM_operatortype_append(OBJECT_OT_correctivesmooth_bind); WM_operatortype_append(OBJECT_OT_meshdeform_bind); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 8841b1955bf..5caa7c71e83 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1554,6 +1554,7 @@ enum { MAKE_LINKS_DUPLICOLLECTION = 5, MAKE_LINKS_MODIFIERS = 6, MAKE_LINKS_FONTS = 7, + MAKE_LINKS_SHADERFX = 8, }; /* Return true if make link data is allowed, false otherwise */ @@ -1589,6 +1590,11 @@ static bool allow_make_links_data(const int type, Object *ob_src, Object *ob_dst return true; } break; + case MAKE_LINKS_SHADERFX: + if ((ob_src->type == OB_GPENCIL) && (ob_dst->type == OB_GPENCIL)) { + return true; + } + break; } return false; } @@ -1720,6 +1726,11 @@ static int make_links_data_exec(bContext *C, wmOperator *op) ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); break; } + case MAKE_LINKS_SHADERFX: + ED_object_shaderfx_link(ob_dst, ob_src); + DEG_id_tag_update(&ob_dst->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + break; } } } @@ -1782,6 +1793,7 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) {MAKE_LINKS_DUPLICOLLECTION, "DUPLICOLLECTION", 0, "Instance Collection", ""}, {MAKE_LINKS_MODIFIERS, "MODIFIERS", 0, "Modifiers", ""}, {MAKE_LINKS_FONTS, "FONTS", 0, "Fonts", ""}, + {MAKE_LINKS_SHADERFX, "EFFECTS", 0, "Effects", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -2515,7 +2527,7 @@ static bool convert_proxy_to_override_poll(bContext *C) return obact != NULL && obact->proxy != NULL; } -static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op)) +static int convert_proxy_to_override_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -2529,6 +2541,15 @@ static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op)) const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, view_layer, ob_proxy); + if (!success) { + BKE_reportf( + op->reports, + RPT_ERROR_INVALID_INPUT, + "Could not create a library override from proxy '%s' (might use already local data?)", + ob_proxy->id.name + 2); + return OPERATOR_CANCELLED; + } + /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ if (success && is_override_instancing_object) { @@ -2544,7 +2565,7 @@ static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op)) void OBJECT_OT_convert_proxy_to_override(wmOperatorType *ot) { /* identifiers */ - ot->name = "Convert Proxy To Override"; + ot->name = "Convert Proxy to Override"; ot->description = "Convert a proxy to a local library override"; ot->idname = "OBJECT_OT_convert_proxy_to_override"; diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c index c5caee5ba08..2b1ac08ec2e 100644 --- a/source/blender/editors/object/object_shader_fx.c +++ b/source/blender/editors/object/object_shader_fx.c @@ -640,3 +640,57 @@ void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot) RNA_def_int( ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the effect to", 0, INT_MAX); } + +/************************ copy shader operator *********************/ + +static int shaderfx_copy_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0); + + ShaderFxData *nfx = BKE_shaderfx_new(fx->type); + if (!nfx) { + return OPERATOR_CANCELLED; + } + + BLI_strncpy(nfx->name, fx->name, sizeof(nfx->name)); + /* Make sure effect data has unique name. */ + BKE_shaderfx_unique_name(&ob->shader_fx, nfx); + + BKE_shaderfx_copydata(fx, nfx); + BLI_insertlinkafter(&ob->shader_fx, fx, nfx); + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, ob); + + return OPERATOR_FINISHED; +} + +static int shaderfx_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (edit_shaderfx_invoke_properties(C, op, event, &retval)) { + return shaderfx_copy_exec(C, op); + } + return retval; +} + +static bool shaderfx_copy_poll(bContext *C) +{ + return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0); +} + +void OBJECT_OT_shaderfx_copy(wmOperatorType *ot) +{ + ot->name = "Copy Effect"; + ot->description = "Duplicate effect at the same position in the stack"; + ot->idname = "OBJECT_OT_shaderfx_copy"; + + ot->invoke = shaderfx_copy_invoke; + ot->exec = shaderfx_copy_exec; + ot->poll = shaderfx_copy_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_shaderfx_properties(ot); +} diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 3d6a6abfe0d..23f1718cb2e 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -741,6 +741,9 @@ const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext * RNA_enum_items_add_value( &item, &totitem, WT_vertex_group_select_item, WT_VGROUP_BONE_SELECT); } + } + + if (BKE_modifiers_is_deformed_by_armature(ob)) { if (selection_mask & (1 << WT_VGROUP_BONE_DEFORM)) { RNA_enum_items_add_value( &item, &totitem, WT_vertex_group_select_item, WT_VGROUP_BONE_DEFORM); diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 017cd63d9d5..f5c3fc17552 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -1203,6 +1203,9 @@ static bool copy_particle_systems_to_object(const bContext *C, #undef PSYS_FROM_FIRST #undef PSYS_FROM_NEXT + if (duplicate_settings) { + DEG_relations_tag_update(bmain); + } DEG_id_tag_update(&ob_to->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, ob_to); return true; diff --git a/source/blender/editors/physics/rigidbody_constraint.c b/source/blender/editors/physics/rigidbody_constraint.c index 4939bf0086b..cb7ca5bd5d1 100644 --- a/source/blender/editors/physics/rigidbody_constraint.c +++ b/source/blender/editors/physics/rigidbody_constraint.c @@ -25,6 +25,7 @@ #include <stdlib.h> #include <string.h> +#include "DNA_collection_types.h" #include "DNA_object_types.h" #include "DNA_rigidbody_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c index cb25363d2b2..4fd304ea71d 100644 --- a/source/blender/editors/physics/rigidbody_object.c +++ b/source/blender/editors/physics/rigidbody_object.c @@ -25,6 +25,7 @@ #include <stdlib.h> #include <string.h> +#include "DNA_collection_types.h" #include "DNA_object_types.h" #include "DNA_rigidbody_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 3dbf70aa4bc..a035ee3e342 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -150,31 +150,31 @@ static void image_buffer_rect_update(RenderJob *rj, } /* xmin here is first subrect x coord, xmax defines subrect width */ - xmin = renrect->xmin + rr->crop; - xmax = renrect->xmax - xmin + rr->crop; + xmin = renrect->xmin; + xmax = renrect->xmax - xmin; if (xmax < 2) { return; } - ymin = renrect->ymin + rr->crop; - ymax = renrect->ymax - ymin + rr->crop; + ymin = renrect->ymin; + ymax = renrect->ymax - ymin; if (ymax < 2) { return; } renrect->ymin = renrect->ymax; } else { - xmin = ymin = rr->crop; - xmax = rr->rectx - 2 * rr->crop; - ymax = rr->recty - 2 * rr->crop; + xmin = ymin = 0; + xmax = rr->rectx; + ymax = rr->recty; } /* xmin ymin is in tile coords. transform to ibuf */ - rxmin = rr->tilerect.xmin + xmin; + rxmin = rr->tilerect.xmin; if (rxmin >= ibuf->x) { return; } - rymin = rr->tilerect.ymin + ymin; + rymin = rr->tilerect.ymin; if (rymin >= ibuf->y) { return; } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 095deccada0..579fd86077e 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -68,6 +68,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_node.h" +#include "BKE_object.h" #include "BKE_scene.h" #include "BKE_texture.h" #include "BKE_world.h" @@ -94,12 +95,16 @@ #include "ED_datafiles.h" #include "ED_render.h" #include "ED_screen.h" +#include "ED_view3d.h" +#include "ED_view3d_offscreen.h" #ifndef NDEBUG /* Used for database init assert(). */ # include "BLI_threads.h" #endif +static void icon_copy_rect(ImBuf *ibuf, uint w, uint h, uint *rect); + ImBuf *get_brush_icon(Brush *brush) { static const int flags = IB_rect | IB_multilayer | IB_metadata; @@ -184,7 +189,7 @@ typedef struct IconPreview { Main *bmain; Scene *scene; void *owner; - ID *id, *id_copy; + ID *id, *id_copy; /* May be NULL! (see ICON_TYPE_PREVIEW case in #ui_icon_ensure_deferred()) */ ListBase sizes; } IconPreview; @@ -336,7 +341,7 @@ static World *preview_get_localized_world(ShaderPreview *sp, World *world) return sp->worldcopy; } -static ID *duplicate_ids(ID *id) +static ID *duplicate_ids(ID *id, const bool allow_failure) { if (id == NULL) { /* Non-ID preview render. */ @@ -344,20 +349,25 @@ static ID *duplicate_ids(ID *id) } switch (GS(id->name)) { + case ID_OB: case ID_MA: case ID_TE: case ID_LA: case ID_WO: { + BLI_assert(BKE_previewimg_id_supports_jobs(id)); ID *id_copy = BKE_id_copy_ex( NULL, id, NULL, LIB_ID_CREATE_LOCAL | LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); return id_copy; } + /* These support threading, but don't need duplicating. */ case ID_IM: case ID_BR: - case ID_SCR: + BLI_assert(BKE_previewimg_id_supports_jobs(id)); return NULL; default: - BLI_assert(!"ID type preview not supported."); + if (!allow_failure) { + BLI_assert(!"ID type preview not supported."); + } return NULL; } } @@ -698,6 +708,132 @@ void ED_preview_draw(const bContext *C, void *idp, void *parentp, void *slotp, r } } +/* **************************** Object preview ****************** */ + +struct ObjectPreviewData { + /* The main for the preview, not of the current file. */ + Main *pr_main; + /* Copy of the object to create the preview for. The copy is for thread safety (and to insert it + * into an own main). */ + Object *object; + int sizex; + int sizey; +}; + +static Object *object_preview_camera_create( + Main *preview_main, ViewLayer *view_layer, Object *preview_object, int sizex, int sizey) +{ + Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera"); + + float rotmat[3][3]; + float dummyscale[3]; + mat4_to_loc_rot_size(camera->loc, rotmat, dummyscale, preview_object->obmat); + + /* Camera is Y up, so needs additional 90deg rotation around X to match object's Z up. */ + float drotmat[3][3]; + axis_angle_to_mat3_single(drotmat, 'X', M_PI_2); + mul_m3_m3_post(rotmat, drotmat); + + camera->rotmode = ROT_MODE_QUAT; + mat3_to_quat(camera->quat, rotmat); + + /* shader_preview_render() does this too. */ + if (sizex > sizey) { + ((Camera *)camera->data)->lens *= (float)sizey / (float)sizex; + } + + return camera; +} + +static Scene *object_preview_scene_create(const struct ObjectPreviewData *preview_data, + Depsgraph **r_depsgraph) +{ + Scene *scene = BKE_scene_add(preview_data->pr_main, "Object preview scene"); + ViewLayer *view_layer = scene->view_layers.first; + Depsgraph *depsgraph = DEG_graph_new( + preview_data->pr_main, scene, view_layer, DAG_EVAL_VIEWPORT); + + BLI_assert(preview_data->object != NULL); + BLI_addtail(&preview_data->pr_main->objects, preview_data->object); + + BKE_collection_object_add(preview_data->pr_main, scene->master_collection, preview_data->object); + + Object *camera_object = object_preview_camera_create(preview_data->pr_main, + view_layer, + preview_data->object, + preview_data->sizex, + preview_data->sizey); + + scene->camera = camera_object; + scene->r.xsch = preview_data->sizex; + scene->r.ysch = preview_data->sizey; + scene->r.size = 100; + + Base *preview_base = BKE_view_layer_base_find(view_layer, preview_data->object); + /* For 'view selected' below. */ + preview_base->flag |= BASE_SELECTED; + + DEG_graph_build_from_view_layer(depsgraph); + DEG_evaluate_on_refresh(depsgraph); + + ED_view3d_camera_to_view_selected(preview_data->pr_main, depsgraph, scene, camera_object); + + BKE_scene_graph_update_tagged(depsgraph, preview_data->pr_main); + + *r_depsgraph = depsgraph; + return scene; +} + +static void object_preview_render(IconPreview *preview, IconPreviewSize *preview_sized) +{ + Main *preview_main = BKE_main_new(); + const float pixelsize_old = U.pixelsize; + char err_out[256] = "unknown"; + + BLI_assert(preview->id_copy && (preview->id_copy != preview->id)); + + struct ObjectPreviewData preview_data = { + .pr_main = preview_main, + /* Act on a copy. */ + .object = (Object *)preview->id_copy, + .sizex = preview_sized->sizex, + .sizey = preview_sized->sizey, + }; + Depsgraph *depsgraph; + Scene *scene = object_preview_scene_create(&preview_data, &depsgraph); + + /* Ownership is now ours. */ + preview->id_copy = NULL; + + U.pixelsize = 2.0f; + + ImBuf *ibuf = ED_view3d_draw_offscreen_imbuf_simple( + depsgraph, + DEG_get_evaluated_scene(depsgraph), + NULL, + OB_SOLID, + DEG_get_evaluated_object(depsgraph, scene->camera), + preview_sized->sizex, + preview_sized->sizey, + IB_rect, + V3D_OFSDRAW_NONE, + R_ALPHAPREMUL, + NULL, + NULL, + err_out); + /* TODO color-management? */ + + U.pixelsize = pixelsize_old; + + if (ibuf) { + icon_copy_rect(ibuf, preview_sized->sizex, preview_sized->sizey, preview_sized->rect); + IMB_freeImBuf(ibuf); + } + + DEG_graph_free(depsgraph); + BKE_main_free(preview_main); +} + /* **************************** new shader preview system ****************** */ /* inside thread, called by renderer, sets job update value */ @@ -1101,6 +1237,8 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat ID *id = sp->id; short idtype = GS(id->name); + BLI_assert(id != NULL); + if (idtype == ID_IM) { Image *ima = (Image *)id; ImBuf *ibuf = NULL; @@ -1188,27 +1326,72 @@ static void common_preview_startjob(void *customdata, } } -/* exported functions */ - -static void icon_preview_add_size(IconPreview *ip, uint *rect, int sizex, int sizey) +/** + * Some ID types already have their own, more focused rendering (only objects right now). This is + * for the other ones, which all share #ShaderPreview and some functions. + */ +static void other_id_types_preview_render(IconPreview *ip, + IconPreviewSize *cur_size, + const bool is_deferred, + short *stop, + short *do_update, + float *progress) { - IconPreviewSize *cur_size = ip->sizes.first, *new_size; + ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview"); + const bool is_render = !is_deferred; + + /* These types don't use the ShaderPreview mess, they have their own types and functions. */ + BLI_assert(!ip->id || !ELEM(GS(ip->id->name), ID_OB)); + + /* construct shader preview from image size and previewcustomdata */ + sp->scene = ip->scene; + sp->owner = ip->owner; + sp->sizex = cur_size->sizex; + sp->sizey = cur_size->sizey; + sp->pr_method = is_render ? PR_ICON_RENDER : PR_ICON_DEFERRED; + sp->pr_rect = cur_size->rect; + sp->id = ip->id; + sp->id_copy = ip->id_copy; + sp->bmain = ip->bmain; + sp->own_id_copy = false; + Material *ma = NULL; - while (cur_size) { - if (cur_size->sizex == sizex && cur_size->sizey == sizey) { - /* requested size is already in list, no need to add it again */ - return; + if (is_render) { + BLI_assert(ip->id); + + /* grease pencil use its own preview file */ + if (GS(ip->id->name) == ID_MA) { + ma = (Material *)ip->id; } - cur_size = cur_size->next; + if ((ma == NULL) || (ma->gp_style == NULL)) { + sp->pr_main = G_pr_main; + } + else { + sp->pr_main = G_pr_main_grease_pencil; + } } - new_size = MEM_callocN(sizeof(IconPreviewSize), "IconPreviewSize"); - new_size->sizex = sizex; - new_size->sizey = sizey; - new_size->rect = rect; + common_preview_startjob(sp, stop, do_update, progress); + shader_preview_free(sp); +} - BLI_addtail(&ip->sizes, new_size); +/* exported functions */ + +/** + * Find the index to map \a icon_size to data in \a preview_image. + */ +static int icon_previewimg_size_index_get(const IconPreviewSize *icon_size, + const PreviewImage *preview_image) +{ + for (int i = 0; i < NUM_ICON_SIZES; i++) { + if ((preview_image->w[i] == icon_size->sizex) && (preview_image->h[i] == icon_size->sizey)) { + return i; + } + } + + BLI_assert(!"The searched icon size does not match any in the preview image"); + return -1; } static void icon_preview_startjob_all_sizes(void *customdata, @@ -1235,41 +1418,43 @@ static void icon_preview_startjob_all_sizes(void *customdata, continue; } - ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview"); - const bool is_render = !(prv->tag & PRV_TAG_DEFFERED); - - /* construct shader preview from image size and previewcustomdata */ - sp->scene = ip->scene; - sp->owner = ip->owner; - sp->sizex = cur_size->sizex; - sp->sizey = cur_size->sizey; - sp->pr_method = is_render ? PR_ICON_RENDER : PR_ICON_DEFERRED; - sp->pr_rect = cur_size->rect; - sp->id = ip->id; - sp->id_copy = ip->id_copy; - sp->bmain = ip->bmain; - sp->own_id_copy = false; - Material *ma = NULL; - - if (is_render) { - BLI_assert(ip->id); - - /* grease pencil use its own preview file */ - if (GS(ip->id->name) == ID_MA) { - ma = (Material *)ip->id; - } +#ifndef NDEBUG + { + int size_index = icon_previewimg_size_index_get(cur_size, prv); + BLI_assert(!BKE_previewimg_is_finished(prv, size_index)); + } +#endif - if ((ma == NULL) || (ma->gp_style == NULL)) { - sp->pr_main = G_pr_main; - } - else { - sp->pr_main = G_pr_main_grease_pencil; - } + if (ip->id && ELEM(GS(ip->id->name), ID_OB)) { + /* Much simpler than the ShaderPreview mess used for other ID types. */ + object_preview_render(ip, cur_size); + } + else { + other_id_types_preview_render( + ip, cur_size, (prv->tag & PRV_TAG_DEFFERED), stop, do_update, progress); + } + } +} + +static void icon_preview_add_size(IconPreview *ip, uint *rect, int sizex, int sizey) +{ + IconPreviewSize *cur_size = ip->sizes.first, *new_size; + + while (cur_size) { + if (cur_size->sizex == sizex && cur_size->sizey == sizey) { + /* requested size is already in list, no need to add it again */ + return; } - common_preview_startjob(sp, stop, do_update, progress); - shader_preview_free(sp); + cur_size = cur_size->next; } + + new_size = MEM_callocN(sizeof(IconPreviewSize), "IconPreviewSize"); + new_size->sizex = sizex; + new_size->sizey = sizey; + new_size->rect = rect; + + BLI_addtail(&ip->sizes, new_size); } static void icon_preview_endjob(void *customdata) @@ -1302,9 +1487,15 @@ static void icon_preview_endjob(void *customdata) if (ip->owner) { PreviewImage *prv_img = ip->owner; prv_img->tag &= ~PRV_TAG_DEFFERED_RENDERING; + + LISTBASE_FOREACH (IconPreviewSize *, icon_size, &ip->sizes) { + int size_index = icon_previewimg_size_index_get(icon_size, prv_img); + BKE_previewimg_finish(prv_img, size_index); + } + if (prv_img->tag & PRV_TAG_DEFFERED_DELETE) { BLI_assert(prv_img->tag & PRV_TAG_DEFFERED); - BKE_previewimg_cached_release_pointer(prv_img); + BKE_previewimg_deferred_release(prv_img); } } } @@ -1333,7 +1524,9 @@ void ED_preview_icon_render(Main *bmain, Scene *scene, ID *id, uint *rect, int s ip.scene = scene; ip.owner = BKE_previewimg_id_ensure(id); ip.id = id; - ip.id_copy = duplicate_ids(id); + /* Control isn't given back to the caller until the preview is done. So we don't need to copy + * the ID to avoid thread races. */ + ip.id_copy = duplicate_ids(id, true); icon_preview_add_size(&ip, rect, sizex, sizey); @@ -1376,7 +1569,7 @@ void ED_preview_icon_job( ip->scene = CTX_data_scene(C); ip->owner = owner; ip->id = id; - ip->id_copy = duplicate_ids(id); + ip->id_copy = duplicate_ids(id, false); icon_preview_add_size(ip, rect, sizex, sizey); @@ -1416,6 +1609,8 @@ void ED_preview_shader_job(const bContext *C, Scene *scene = CTX_data_scene(C); short id_type = GS(id->name); + BLI_assert(BKE_previewimg_id_supports_jobs(id)); + /* Use workspace render only for buttons Window, * since the other previews are related to the datablock. */ @@ -1445,7 +1640,7 @@ void ED_preview_shader_job(const bContext *C, sp->sizey = sizey; sp->pr_method = method; sp->id = id; - sp->id_copy = duplicate_ids(id); + sp->id_copy = duplicate_ids(id, false); sp->own_id_copy = true; sp->parent = parent; sp->slot = slot; diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 244ebea5bbe..fb7606d1fe5 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -649,8 +649,6 @@ static eContextResult screen_ctx_selected_editable_sequences(const bContext *C, } static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContextDataResult *result) { - wmWindow *win = CTX_wm_window(C); - Scene *scene = WM_window_get_active_scene(win); bAnimContext ac; if (ANIM_animdata_get_context(C, &ac) != 0) { ListBase anim_data = {NULL, NULL}; @@ -663,7 +661,7 @@ static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContext NlaTrack *nlt = (NlaTrack *)ale->data; LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) { if (strip->flag & NLASTRIP_FLAG_SELECT) { - CTX_data_list_add(result, &scene->id, &RNA_NlaStrip, strip); + CTX_data_list_add(result, ale->id, &RNA_NlaStrip, strip); } } } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 6be2fb8004b..be52874ed0b 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1133,12 +1133,11 @@ void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene) ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type) { - wmWindow *win = CTX_wm_window(C); ScrArea *newsa = NULL; SpaceLink *newsl; if (!area || area->full == NULL) { - newsa = ED_screen_state_toggle(C, win, area, SCREENMAXIMIZED); + newsa = ED_screen_state_maximized_create(C); } if (!newsa) { @@ -1149,11 +1148,11 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type) newsl = newsa->spacedata.first; /* Tag the active space before changing, so we can identify it when user wants to go back. */ - if ((newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) { + if (newsl && (newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) { newsl->link_flag |= SPACE_FLAG_TYPE_WAS_ACTIVE; } - ED_area_newspace(C, newsa, type, newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY); + ED_area_newspace(C, newsa, type, (newsl && newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY)); return newsa; } @@ -1217,13 +1216,108 @@ void ED_screen_full_restore(bContext *C, ScrArea *area) } /** - * this function toggles: if area is maximized/full then the parent will be restored + * \param toggle_area: If this is set, its space data will be swapped with the one of the new emtpy + * area, when toggling back it can be swapped back again. + * \return The newly created screen with the non-normal area. + */ +static bScreen *screen_state_to_nonnormal(bContext *C, + wmWindow *win, + ScrArea *toggle_area, + int state) +{ + Main *bmain = CTX_data_main(C); + WorkSpace *workspace = WM_window_get_active_workspace(win); + + /* change from SCREENNORMAL to new state */ + WorkSpaceLayout *layout_new; + ScrArea *newa; + char newname[MAX_ID_NAME - 2]; + + BLI_assert(ELEM(state, SCREENMAXIMIZED, SCREENFULL)); + + bScreen *oldscreen = WM_window_get_active_screen(win); + + oldscreen->state = state; + BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal"); + + layout_new = ED_workspace_layout_add(bmain, workspace, win, newname); + + bScreen *screen = BKE_workspace_layout_screen_get(layout_new); + screen->state = state; + screen->redraws_flag = oldscreen->redraws_flag; + screen->temp = oldscreen->temp; + screen->flag = oldscreen->flag; + + /* timer */ + screen->animtimer = oldscreen->animtimer; + oldscreen->animtimer = NULL; + + newa = (ScrArea *)screen->areabase.first; + + /* swap area */ + if (toggle_area) { + ED_area_data_swap(newa, toggle_area); + newa->flag = toggle_area->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */ + } + + if (state == SCREENFULL) { + /* temporarily hide global areas */ + LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) { + glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN; + } + /* temporarily hide the side panels/header */ + LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) { + region->flagfullscreen = region->flag; + + if (ELEM(region->regiontype, + RGN_TYPE_UI, + RGN_TYPE_HEADER, + RGN_TYPE_TOOL_HEADER, + RGN_TYPE_FOOTER, + RGN_TYPE_TOOLS, + RGN_TYPE_NAV_BAR, + RGN_TYPE_EXECUTE)) { + region->flag |= RGN_FLAG_HIDDEN; + } + } + } + + if (toggle_area) { + toggle_area->full = oldscreen; + } + newa->full = oldscreen; + + ED_screen_change(C, screen); + ED_area_tag_refresh(newa); + + return screen; +} + +/** + * Create a new temporary screen with a maximized, empty area. + * This can be closed with #ED_screen_state_toggle(). + * + * Use this to just create a new maximized screen/area, rather than maximizing an existing one. + * Otherwise, maximize with #ED_screen_state_toggle(). + */ +ScrArea *ED_screen_state_maximized_create(bContext *C) +{ + bScreen *screen = screen_state_to_nonnormal(C, CTX_wm_window(C), NULL, SCREENMAXIMIZED); + return screen->areabase.first; +} + +/** + * This function toggles: if area is maximized/full then the parent will be restored. + * + * Use #ED_screen_state_maximized_create() if you do not want the toggle behavior when changing to + * a maximized area. I.e. if you just want to open a new maximized screen/area, not maximize a + * specific area. In the former case, space data of the maximized and non-maximized area should be + * independent, in the latter it should be the same. * * \warning \a area may be freed. */ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const short state) { - Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); WorkSpace *workspace = WM_window_get_active_workspace(win); @@ -1257,7 +1351,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const screen->state = SCREENNORMAL; screen->flag = oldscreen->flag; - /* find old area to restore from */ + /* Find old area we may have swapped dummy space data to. It's swapped back here. */ ScrArea *fullsa = NULL; LISTBASE_FOREACH (ScrArea *, old, &screen->areabase) { /* area to restore from is always first */ @@ -1271,13 +1365,6 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const area->full = NULL; - if (fullsa == NULL) { - if (G.debug & G_DEBUG) { - printf("%s: something wrong in areafullscreen\n", __func__); - } - return NULL; - } - if (state == SCREENFULL) { /* unhide global areas */ LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) { @@ -1289,14 +1376,16 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const } } - ED_area_data_swap(fullsa, area); + if (fullsa) { + ED_area_data_swap(fullsa, area); + ED_area_tag_refresh(fullsa); + } /* animtimer back */ screen->animtimer = oldscreen->animtimer; oldscreen->animtimer = NULL; ED_screen_change(C, screen); - ED_area_tag_refresh(fullsa); BKE_workspace_layout_remove(CTX_data_main(C), workspace, layout_old); @@ -1307,68 +1396,16 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const screen->skip_handling = true; } else { - /* change from SCREENNORMAL to new state */ - WorkSpaceLayout *layout_new; - ScrArea *newa; - char newname[MAX_ID_NAME - 2]; - - BLI_assert(ELEM(state, SCREENMAXIMIZED, SCREENFULL)); - - bScreen *oldscreen = WM_window_get_active_screen(win); - - oldscreen->state = state; - BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal"); - - layout_new = ED_workspace_layout_add(bmain, workspace, win, newname); - - screen = BKE_workspace_layout_screen_get(layout_new); - screen->state = state; - screen->redraws_flag = oldscreen->redraws_flag; - screen->temp = oldscreen->temp; - screen->flag = oldscreen->flag; - - /* timer */ - screen->animtimer = oldscreen->animtimer; - oldscreen->animtimer = NULL; + ScrArea *toggle_area = area; /* use random area when we have no active one, e.g. when the * mouse is outside of the window and we open a file browser */ - if (!area || area->global) { - area = oldscreen->areabase.first; + if (!toggle_area || toggle_area->global) { + bScreen *oldscreen = WM_window_get_active_screen(win); + toggle_area = oldscreen->areabase.first; } - newa = (ScrArea *)screen->areabase.first; - - /* copy area */ - ED_area_data_swap(newa, area); - newa->flag = area->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */ - - if (state == SCREENFULL) { - /* temporarily hide global areas */ - LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) { - glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN; - } - /* temporarily hide the side panels/header */ - LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) { - region->flagfullscreen = region->flag; - - if (ELEM(region->regiontype, - RGN_TYPE_UI, - RGN_TYPE_HEADER, - RGN_TYPE_TOOL_HEADER, - RGN_TYPE_FOOTER, - RGN_TYPE_TOOLS, - RGN_TYPE_NAV_BAR, - RGN_TYPE_EXECUTE)) { - region->flag |= RGN_FLAG_HIDDEN; - } - } - } - - area->full = oldscreen; - newa->full = oldscreen; - - ED_screen_change(C, screen); + screen = screen_state_to_nonnormal(C, win, toggle_area, state); } /* XXX bad code: setscreen() ends with first area active. fullscreen render assumes this too */ @@ -1412,9 +1449,6 @@ ScrArea *ED_screen_temp_space_open(bContext *C, area->flag |= AREA_FLAG_STACKED_FULLSCREEN; ((SpaceLink *)area->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY; } - else if (ctx_area != NULL && ctx_area->spacetype == space_type) { - area = ED_screen_state_toggle(C, CTX_wm_window(C), ctx_area, SCREENMAXIMIZED); - } else { area = ED_screen_full_newspace(C, ctx_area, (int)space_type); ((SpaceLink *)area->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 72b3b344813..51687d5de1d 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3918,7 +3918,7 @@ static void SCREEN_OT_region_quadview(wmOperatorType *ot) { /* identifiers */ ot->name = "Toggle Quad View"; - ot->description = "Split selected area into camera, front, right & top views"; + ot->description = "Split selected area into camera, front, right, and top views"; ot->idname = "SCREEN_OT_region_quadview"; /* api callbacks */ @@ -5510,6 +5510,8 @@ void ED_operatortypes_screen(void) WM_operatortype_append(ED_OT_undo_history); WM_operatortype_append(ED_OT_flush_edits); + WM_operatortype_append(ED_OT_lib_id_load_custom_preview); + WM_operatortype_append(ED_OT_lib_id_generate_preview); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 9bf3d2610d8..fff8d27ef5b 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -32,6 +32,7 @@ set(INC ../../windowmanager ../../../../intern/atomic ../../../../intern/clog + ../../../../intern/eigen ../../../../intern/glew-mx ../../../../intern/guardedalloc ) diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 3a6b91443a0..324fd5d3075 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -40,6 +40,7 @@ #include "IMB_imbuf_types.h" #include "DNA_brush_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" @@ -934,7 +935,7 @@ void PAINT_OT_grab_clone(wmOperatorType *ot) -FLT_MAX, FLT_MAX, "Delta", - "Delta offset of clone image in 0.0..1.0 coordinates", + "Delta offset of clone image in 0.0 to 1.0 coordinates", -1.0f, 1.0f); } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 98f4b4013cb..cca4ffd4d78 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -6781,7 +6781,7 @@ static bool add_simple_uvs_poll(bContext *C) void PAINT_OT_add_simple_uvs(wmOperatorType *ot) { /* identifiers */ - ot->name = "Add simple UVs"; + ot->name = "Add Simple UVs"; ot->description = "Add cube map uvs on mesh"; ot->idname = "PAINT_OT_add_simple_uvs"; diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 175d98ba9aa..3ca0d853d6a 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -43,7 +43,6 @@ struct wmEvent; struct wmKeyConfig; struct wmOperator; struct wmOperatorType; -struct wmWindowManager; enum ePaintMode; enum ePaintSymmetryFlags; diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 17690757fa5..92c78a674f0 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1552,6 +1552,11 @@ static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (ss->totvert == 0) { + /* No geometry to trim or to detect a valid position for the trimming shape. */ + return OPERATOR_CANCELLED; + } + SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op); if (!sgcontext) { return OPERATOR_CANCELLED; @@ -1589,6 +1594,11 @@ static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (ss->totvert == 0) { + /* No geometry to trim or to detect a valid position for the trimming shape. */ + return OPERATOR_CANCELLED; + } + SculptGestureContext *sgcontext = sculpt_gesture_init_from_lasso(C, op); if (!sgcontext) { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index fd7ec1da497..38d2bed7d97 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1227,6 +1227,7 @@ static bool sculpt_tool_is_proxy_used(const char sculpt_tool) SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER, SCULPT_TOOL_POSE, + SCULPT_TOOL_DISPLACEMENT_SMEAR, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_CLOTH, SCULPT_TOOL_PAINT, @@ -2362,6 +2363,7 @@ static float brush_strength(const Sculpt *sd, final_pressure = pressure * pressure; return final_pressure * overlap * feather; case SCULPT_TOOL_SMEAR: + case SCULPT_TOOL_DISPLACEMENT_SMEAR: return alpha * pressure * overlap * feather; case SCULPT_TOOL_CLAY_STRIPS: /* Clay Strips needs less strength to compensate the curve. */ @@ -3103,6 +3105,147 @@ static void do_displacement_eraser_brush(Sculpt *sd, Object *ob, PBVHNode **node /** \} */ +/** \name Sculpt Multires Displacement Smear Brush + * \{ */ + +static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + float current_disp[3]; + float current_disp_norm[3]; + float interp_limit_surface_disp[3]; + + copy_v3_v3(interp_limit_surface_disp, ss->cache->prev_displacement[vd.index]); + + switch (brush->smear_deform_type) { + case BRUSH_SMEAR_DEFORM_DRAG: + sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); + break; + case BRUSH_SMEAR_DEFORM_PINCH: + sub_v3_v3v3(current_disp, ss->cache->location, vd.co); + break; + case BRUSH_SMEAR_DEFORM_EXPAND: + sub_v3_v3v3(current_disp, vd.co, ss->cache->location); + break; + } + + normalize_v3_v3(current_disp_norm, current_disp); + mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); + + float weights_accum = 1.0f; + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + float vertex_disp[3]; + float vertex_disp_norm[3]; + float neighbor_limit_co[3]; + SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co); + sub_v3_v3v3(vertex_disp, + ss->cache->limit_surface_co[ni.index], + ss->cache->limit_surface_co[vd.index]); + const float *neighbor_limit_surface_disp = ss->cache->prev_displacement[ni.index]; + normalize_v3_v3(vertex_disp_norm, vertex_disp); + if (dot_v3v3(current_disp_norm, vertex_disp_norm) < 0.0f) { + const float disp_interp = clamp_f( + -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f); + madd_v3_v3fl(interp_limit_surface_disp, neighbor_limit_surface_disp, disp_interp); + weights_accum += disp_interp; + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + mul_v3_fl(interp_limit_surface_disp, 1.0f / weights_accum); + + float new_co[3]; + add_v3_v3v3(new_co, ss->cache->limit_surface_co[vd.index], interp_limit_surface_disp); + interp_v3_v3v3(vd.co, vd.co, new_co, fade); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_displacement_smear_store_prev_disp_task_cb_ex( + void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sub_v3_v3v3(ss->cache->prev_displacement[vd.index], + SCULPT_vertex_co_get(ss, vd.index), + ss->cache->limit_surface_co[vd.index]); + } + BKE_pbvh_vertex_iter_end; +} + +static void do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + + BKE_curvemapping_init(brush->curve); + + const int totvert = SCULPT_vertex_count_get(ss); + if (!ss->cache->prev_displacement) { + ss->cache->prev_displacement = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "prev displacement"); + ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co"); + for (int i = 0; i < totvert; i++) { + SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]); + sub_v3_v3v3(ss->cache->prev_displacement[i], + SCULPT_vertex_co_get(ss, i), + ss->cache->limit_surface_co[i]); + } + } + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range( + 0, totnode, &data, do_displacement_smear_store_prev_disp_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_displacement_smear_brush_task_cb_ex, &settings); +} + +/** \} */ + static void do_draw_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) @@ -5742,32 +5885,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); } else if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { - if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) { - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)), - .original = false, - .ignore_fully_ineffective = false, - .center = ss->cache->initial_location, - }; - BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); - } - if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC) { - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)), - .original = false, - .ignore_fully_ineffective = false, - .center = ss->cache->location, - }; - BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); - } - else { - /* Gobal simulation, get all nodes. */ - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - } + nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode); } else { const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : @@ -5805,6 +5923,17 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe } } + /* Initialize automasking cache. For anchored brushes with spherical falloff, we start off with + * zero radius, thus we have no pbvh nodes on the first brush step. */ + if (totnode || + ((brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) && (brush->flag & BRUSH_ANCHORED))) { + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + if (SCULPT_is_automasking_enabled(sd, ss, brush)) { + ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob); + } + } + } + /* Only act if some verts are inside the brush area. */ if (totnode) { float location[3]; @@ -5828,12 +5957,6 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe update_brush_local_mat(sd, ob); } - if (SCULPT_stroke_is_first_brush_step(ss->cache)) { - if (SCULPT_is_automasking_enabled(sd, ss, brush)) { - ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob); - } - } - if (brush->sculpt_tool == SCULPT_TOOL_POSE && SCULPT_stroke_is_first_brush_step(ss->cache)) { SCULPT_pose_brush_init(sd, ob, ss, brush); } @@ -5952,6 +6075,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe case SCULPT_TOOL_DISPLACEMENT_ERASER: do_displacement_eraser_brush(sd, ob, nodes, totnode); break; + case SCULPT_TOOL_DISPLACEMENT_SMEAR: + do_displacement_smear_brush(sd, ob, nodes, totnode); + break; case SCULPT_TOOL_PAINT: SCULPT_do_paint_brush(sd, ob, nodes, totnode); break; @@ -6515,6 +6641,8 @@ static const char *sculpt_tool_name(Sculpt *sd) return "Draw Face Sets"; case SCULPT_TOOL_DISPLACEMENT_ERASER: return "Multires Displacement Eraser"; + case SCULPT_TOOL_DISPLACEMENT_SMEAR: + return "Multires Displacement Smear"; case SCULPT_TOOL_PAINT: return "Paint Brush"; case SCULPT_TOOL_SMEAR: @@ -6535,6 +6663,8 @@ void SCULPT_cache_free(StrokeCache *cache) MEM_SAFE_FREE(cache->layer_displacement_factor); MEM_SAFE_FREE(cache->prev_colors); MEM_SAFE_FREE(cache->detail_directions); + MEM_SAFE_FREE(cache->prev_displacement); + MEM_SAFE_FREE(cache->limit_surface_co); if (cache->pose_ik_chain) { SCULPT_pose_ik_chain_free(cache->pose_ik_chain); @@ -6706,6 +6836,7 @@ static void sculpt_update_cache_invariants( SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SIMPLIFY, + SCULPT_TOOL_DISPLACEMENT_SMEAR, SCULPT_TOOL_DISPLACEMENT_ERASER) && (sd->gravity_factor > 0.0f)); /* Get gravity vector in world space. */ @@ -8685,7 +8816,7 @@ static int sculpt_sample_color_invoke(bContext *C, static void SCULPT_OT_sample_color(wmOperatorType *ot) { /* identifiers */ - ot->name = "Sample color"; + ot->name = "Sample Color"; ot->idname = "SCULPT_OT_sample_color"; ot->description = "Sample the vertex color of the active vertex"; diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index f8165890cc4..0ac0d796ca4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -119,6 +119,43 @@ static void cloth_brush_simulation_location_get(SculptSession *ss, copy_v3_v3(r_location, ss->cache->location); } +PBVHNode **SCULPT_cloth_brush_affected_nodes_gather(SculptSession *ss, + Brush *brush, + int *r_totnode) +{ + BLI_assert(ss->cache); + BLI_assert(brush->sculpt_tool == SCULPT_TOOL_CLOTH); + PBVHNode **nodes = NULL; + + switch (brush->cloth_simulation_area_type) { + case BRUSH_CLOTH_SIMULATION_AREA_LOCAL: { + SculptSearchSphereData data = { + .ss = ss, + .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)), + .original = false, + .ignore_fully_ineffective = false, + .center = ss->cache->initial_location, + }; + BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); + } break; + case BRUSH_CLOTH_SIMULATION_AREA_GLOBAL: + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, r_totnode); + break; + case BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC: { + SculptSearchSphereData data = { + .ss = ss, + .radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)), + .original = false, + .ignore_fully_ineffective = false, + .center = ss->cache->location, + }; + BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); + } break; + } + + return nodes; +} + static float cloth_brush_simulation_falloff_get(const Brush *brush, const float radius, const float location[3], diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index ddf5b39f080..aa1d407dc24 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -363,7 +363,7 @@ void SCULPT_OT_sample_detail_size(wmOperatorType *ot) 0, SHRT_MAX, "Location", - "Screen Coordinates of sampling", + "Screen coordinates of sampling", 0, SHRT_MAX); RNA_def_enum(ot->srna, diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index ad42750bb92..1fba958d695 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -41,6 +41,7 @@ #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_mesh.h" +#include "BKE_mesh_fair.h" #include "BKE_mesh_mapping.h" #include "BKE_multires.h" #include "BKE_node.h" @@ -1024,6 +1025,8 @@ typedef enum eSculptFaceSetEditMode { SCULPT_FACE_SET_EDIT_GROW = 0, SCULPT_FACE_SET_EDIT_SHRINK = 1, SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY = 2, + SCULPT_FACE_SET_EDIT_FAIR_POSITIONS = 3, + SCULPT_FACE_SET_EDIT_FAIR_TANGENCY = 4, } eSculptFaceSetEditMode; static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { @@ -1048,6 +1051,22 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { "Delete Geometry", "Deletes the faces that are assigned to the Face Set", }, + { + SCULPT_FACE_SET_EDIT_FAIR_POSITIONS, + "FAIR_POSITIONS", + 0, + "Fair Positions", + "Creates a smooth as possible geometry patch from the Face Set minimizing changes in " + "vertex positions", + }, + { + SCULPT_FACE_SET_EDIT_FAIR_TANGENCY, + "FAIR_TANGENCY", + 0, + "Fair Tangency", + "Creates a smooth as possible geometry patch from the Face Set minimizing changes in " + "vertex tangents", + }, {0, NULL, 0, NULL, NULL}, }; @@ -1181,6 +1200,29 @@ static void sculpt_face_set_delete_geometry(Object *ob, BM_mesh_free(bm); } +static void sculpt_face_set_edit_fair_face_set(Object *ob, + const int active_face_set_id, + const int fair_order) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + + Mesh *mesh = ob->data; + bool *fair_vertices = MEM_malloc_arrayN(sizeof(bool), totvert, "fair vertices"); + + SCULPT_boundary_info_ensure(ob); + + for (int i = 0; i < totvert; i++) { + fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) && + SCULPT_vertex_has_face_set(ss, i, active_face_set_id) && + SCULPT_vertex_has_unique_face_set(ss, i); + } + + MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); + BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order); + MEM_freeN(fair_vertices); +} + static void sculpt_face_set_apply_edit(Object *ob, const int active_face_set_id, const int mode, @@ -1204,6 +1246,12 @@ static void sculpt_face_set_apply_edit(Object *ob, case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY: sculpt_face_set_delete_geometry(ob, ss, active_face_set_id, modify_hidden); break; + case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS: + sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_POSITION); + break; + case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY: + sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_TANGENCY); + break; } } @@ -1230,6 +1278,16 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss, return false; } } + + if (ELEM(mode, SCULPT_FACE_SET_EDIT_FAIR_POSITIONS, SCULPT_FACE_SET_EDIT_FAIR_TANGENCY)) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + /* TODO: Multires topology representation using grids and duplicates can't be used directly + * by the fair algorithm. Multires topology needs to be exposed in a different way or + * converted to a mesh for this operation. */ + return false; + } + } + return true; } @@ -1287,11 +1345,38 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob, MEM_freeN(nodes); } +static void sculpt_face_set_edit_modify_coordinates(bContext *C, + Object *ob, + const int active_face_set, + const eSculptFaceSetEditMode mode) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptSession *ss = ob->sculpt; + PBVH *pbvh = ss->pbvh; + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + SCULPT_undo_push_begin(ob, "face set edit"); + for (int i = 0; i < totnode; i++) { + BKE_pbvh_node_mark_update(nodes[i]); + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COORDS); + } + sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, false); + + if (ss->deform_modifiers_active || ss->shapekey_active) { + SCULPT_flush_stroke_deform(sd, ob, true); + } + SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS); + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); + SCULPT_undo_push_end(); + MEM_freeN(nodes); +} + static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); const int mode = RNA_enum_get(op->ptr, "mode"); const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden"); @@ -1320,6 +1405,10 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven case SCULPT_FACE_SET_EDIT_SHRINK: sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden); break; + case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS: + case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY: + sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode); + break; } SCULPT_tag_update_overlays(C); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index f3c07a86201..76a6b05cdff 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -328,9 +328,9 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* rna */ - RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter type", ""); + RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter Type", ""); RNA_def_float( - ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f); + ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); PropertyRNA *prop = RNA_def_float_color( ot->srna, "fill_color", 3, NULL, 0.0f, FLT_MAX, "Fill Color", "", 0.0f, 1.0f); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index ddad6bef7fd..0297ed73dd4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -80,12 +80,12 @@ static EnumPropertyItem prop_mask_filter_types[] = { {MASK_FILTER_CONTRAST_INCREASE, "CONTRAST_INCREASE", 0, - "Increase contrast", + "Increase Contrast", "Increase the contrast of the paint mask"}, {MASK_FILTER_CONTRAST_DECREASE, "CONTRAST_DECREASE", 0, - "Decrease contrast", + "Decrease Contrast", "Decrease the contrast of the paint mask"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 02d4be20e1b..e11894a8c01 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -771,15 +771,15 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) "type", prop_mesh_filter_types, MESH_FILTER_INFLATE, - "Filter type", + "Filter Type", "Operation that is going to be applied to the mesh"); RNA_def_float( - ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f); + ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); RNA_def_enum_flag(ot->srna, "deform_axis", prop_mesh_filter_deform_axis_items, MESH_FILTER_DEFORM_X | MESH_FILTER_DEFORM_Y | MESH_FILTER_DEFORM_Z, - "Deform axis", + "Deform Axis", "Apply the deformation in the selected axis"); RNA_def_enum(ot->srna, "orientation", diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 3b48207f461..d1e17c7e59b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -39,7 +39,6 @@ struct AutomaskingCache; struct KeyBlock; struct Object; -struct SculptPoseIKChainSegment; struct SculptUndoNode; struct bContext; @@ -423,6 +422,10 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr, const float outline_col[3], float outline_alpha); +PBVHNode **SCULPT_cloth_brush_affected_nodes_gather(SculptSession *ss, + Brush *brush, + int *r_totnode); + BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush) { return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && ELEM(brush->cloth_deform_type, @@ -452,7 +455,7 @@ BLI_INLINE bool SCULPT_tool_needs_all_pbvh_nodes(const Brush *brush) if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { /* Boundary needs all nodes because it is not possible to know where the boundary * deformation is going to be propagated before calculating it. */ - /* TODO: after calculating the boudnary info in the first iteration, it should be + /* TODO: after calculating the boundary info in the first iteration, it should be * possible to get the nodes that have vertices included in any boundary deformation * and cache them. */ return true; @@ -923,6 +926,10 @@ typedef struct StrokeCache { float (*prev_colors)[4]; + /* Multires Displacement Smear. */ + float (*prev_displacement)[3]; + float (*limit_surface_co)[3]; + /* The rest is temporary storage that isn't saved as a property */ bool first_time; /* Beginning of stroke may do some things special */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 167215b3813..d186cafb857 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -1827,7 +1827,7 @@ static const EnumPropertyItem prop_actkeys_mirror_types[] = { {ACTKEYS_MIRROR_XAXIS, "XAXIS", 0, - "By Values Over Value=0", + "By Values Over Zero Value", "Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"}, {ACTKEYS_MIRROR_MARKER, "MARKER", diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index ab5f1e0ab22..98e39520e99 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1325,8 +1325,8 @@ void ACTION_OT_select_less(wmOperatorType *ot) /* defines for left-right select tool */ static const EnumPropertyItem prop_actkeys_leftright_select_types[] = { {ACTKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""}, - {ACTKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""}, - {ACTKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""}, + {ACTKEYS_LRSEL_LEFT, "LEFT", 0, "Before Current Frame", ""}, + {ACTKEYS_LRSEL_RIGHT, "RIGHT", 0, "After Current Frame", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 10ce7b81954..0fe94ec382c 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -40,6 +40,7 @@ #include "ED_anim_api.h" #include "ED_armature.h" +#include "ED_asset.h" #include "ED_clip.h" #include "ED_curve.h" #include "ED_fileselect.h" @@ -105,6 +106,7 @@ void ED_spacetypes_init(void) ED_operatortypes_screen(); ED_operatortypes_anim(); ED_operatortypes_animchannels(); + ED_operatortypes_asset(); ED_operatortypes_gpencil(); ED_operatortypes_object(); ED_operatortypes_lattice(); diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index fa3e6a51036..c71e5e49d8d 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -51,6 +51,7 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_GEOMETRY_NODES) + add_definitions(-DWITH_POINT_CLOUD) add_definitions(-DWITH_HAIR_NODES) endif() diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 8b39995a5c9..c1f29231f96 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -246,9 +246,11 @@ static bool buttons_context_path_data(ButsContextPath *path, int type) return true; } #endif +#ifdef WITH_POINT_CLOUD if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) { return true; } +#endif if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) { return true; } @@ -812,7 +814,9 @@ const char *buttons_context_dir[] = { #ifdef WITH_HAIR_NODES "hair", #endif +#ifdef WITH_POINT_CLOUD "pointcloud", +#endif "volume", NULL, }; @@ -822,6 +826,11 @@ int /*eContextResult*/ buttons_context(const bContext *C, bContextDataResult *result) { SpaceProperties *sbuts = CTX_wm_space_properties(C); + if (sbuts && sbuts->path == NULL) { + /* path is cleared for SCREEN_OT_redo_last, when global undo does a file-read which clears the + * path (see lib_link_workspace_layout_restore). */ + buttons_context_compute(C, sbuts); + } ButsContextPath *path = sbuts ? sbuts->path : NULL; if (!path) { @@ -899,10 +908,12 @@ int /*eContextResult*/ buttons_context(const bContext *C, return CTX_RESULT_OK; } #endif +#ifdef WITH_POINT_CLOUD if (CTX_data_equals(member, "pointcloud")) { set_pointer_type(path, result, &RNA_PointCloud); return CTX_RESULT_OK; } +#endif if (CTX_data_equals(member, "volume")) { set_pointer_type(path, result, &RNA_Volume); return CTX_RESULT_OK; diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h index 0a0846cf216..74e7bc11c26 100644 --- a/source/blender/editors/space_buttons/buttons_intern.h +++ b/source/blender/editors/space_buttons/buttons_intern.h @@ -35,7 +35,6 @@ struct bContext; struct bContextDataResult; struct bNode; struct bNodeTree; -struct uiLayout; struct wmOperatorType; struct SpaceProperties_Runtime { diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index 8bdc2ed993f..8f57abf83ae 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -338,7 +338,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) is_relative = BLI_path_is_rel(str); } - if (UNLIKELY(ptr.data == &U)) { + if (UNLIKELY(ptr.data == &U || is_userdef)) { is_relative = false; } diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 3f00e3114a5..8f36ccb019a 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -859,7 +859,7 @@ void CLIP_OT_view_zoom_out(wmOperatorType *ot) -FLT_MAX, FLT_MAX, "Location", - "Cursor location in normalized (0.0-1.0) coordinates", + "Cursor location in normalized (0.0 to 1.0) coordinates", -10.0f, 10.0f); RNA_def_property_flag(prop, PROP_HIDDEN); diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index a54faa41122..9b8e9e0e871 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -164,12 +164,12 @@ static bool id_drop_poll(bContext *UNUSED(C), const wmEvent *UNUSED(event), const char **UNUSED(tooltip)) { - return WM_drag_ID(drag, 0) != NULL; + return WM_drag_get_local_ID(drag, 0) != NULL; } static void id_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_ID(drag, 0); + ID *id = WM_drag_get_local_ID(drag, 0); /* copy drag path to properties */ char *text = RNA_path_full_ID_py(G_MAIN, id); diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index e3bdda7c480..f2f7f9d82f9 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -25,6 +25,7 @@ #include <math.h> #include <string.h> +#include "BLI_alloca.h" #include "BLI_blenlib.h" #include "BLI_fileops_types.h" #include "BLI_math.h" @@ -134,6 +135,7 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh } static void file_draw_icon(uiBlock *block, + const FileDirEntry *file, const char *path, int sx, int sy, @@ -157,8 +159,29 @@ static void file_draw_icon(uiBlock *block, UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path)); if (drag) { - /* path is no more static, cannot give it directly to but... */ - UI_but_drag_set_path(but, BLI_strdup(path), true); + /* TODO duplicated from file_draw_preview(). */ + ID *id; + + if ((id = filelist_file_get_id(file))) { + UI_but_drag_set_id(but, id); + } + else if (file->typeflag & FILE_TYPE_ASSET) { + ImBuf *preview_image = filelist_file_getimage(file); + char blend_path[FILE_MAX_LIBEXTRA]; + if (BLO_library_path_explode(path, blend_path, NULL, NULL)) { + UI_but_drag_set_asset(but, + file->name, + BLI_strdup(blend_path), + file->blentype, + icon, + preview_image, + UI_DPI_FAC); + } + } + else { + /* path is no more static, cannot give it directly to but... */ + UI_but_drag_set_path(but, BLI_strdup(path), true); + } } } @@ -200,6 +223,65 @@ static void file_draw_string(int sx, }); } +/** + * \param r_sx, r_sy: The lower right corner of the last line drawn. AKA the cursor position on + * completion. + */ +static void file_draw_string_multiline(int sx, + int sy, + const char *string, + int wrap_width, + int line_height, + const uchar text_col[4], + int *r_sx, + int *r_sy) +{ + rcti rect; + + if (string[0] == '\0' || wrap_width < 1) { + return; + } + + const uiStyle *style = UI_style_get(); + int font_id = style->widgetlabel.uifont_id; + int len = strlen(string); + + rctf textbox; + BLF_wordwrap(font_id, wrap_width); + BLF_enable(font_id, BLF_WORD_WRAP); + BLF_boundbox(font_id, string, len, &textbox); + BLF_disable(font_id, BLF_WORD_WRAP); + + /* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict + * (for buttons it works) */ + rect.xmin = sx; + rect.xmax = sx + wrap_width; + /* Need to increase the clipping rect by one more line, since the #UI_fontstyle_draw_ex() will + * actually start drawing at (ymax - line-height). */ + rect.ymin = sy - round_fl_to_int(BLI_rctf_size_y(&textbox)) - line_height; + rect.ymax = sy; + + struct ResultBLF result; + UI_fontstyle_draw_ex(&style->widget, + &rect, + string, + text_col, + &(struct uiFontStyleDraw_Params){ + .align = UI_STYLE_TEXT_LEFT, + .word_wrap = true, + }, + len, + NULL, + NULL, + &result); + if (r_sx) { + *r_sx = result.width; + } + if (r_sy) { + *r_sy = rect.ymin + line_height; + } +} + void file_calc_previews(const bContext *C, ARegion *region) { SpaceFile *sfile = CTX_wm_space_file(C); @@ -210,6 +292,7 @@ void file_calc_previews(const bContext *C, ARegion *region) } static void file_draw_preview(uiBlock *block, + const FileDirEntry *file, const char *path, int sx, int sy, @@ -218,7 +301,6 @@ static void file_draw_preview(uiBlock *block, const int icon, FileLayout *layout, const bool is_icon, - const int typeflags, const bool drag, const bool dimmed, const bool is_link) @@ -232,7 +314,7 @@ static void file_draw_preview(uiBlock *block, float scale; int ex, ey; bool show_outline = !is_icon && - (typeflags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER)); + (file->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER)); BLI_assert(imb != NULL); @@ -273,14 +355,14 @@ static void file_draw_preview(uiBlock *block, float col[4] = {1.0f, 1.0f, 1.0f, 1.0f}; if (is_icon) { - if (typeflags & FILE_TYPE_DIR) { + if (file->typeflag & FILE_TYPE_DIR) { UI_GetThemeColor4fv(TH_ICON_FOLDER, col); } else { UI_GetThemeColor4fv(TH_TEXT, col); } } - else if (typeflags & FILE_TYPE_FTFONT) { + else if (file->typeflag & FILE_TYPE_FTFONT) { UI_GetThemeColor4fv(TH_TEXT, col); } @@ -288,7 +370,7 @@ static void file_draw_preview(uiBlock *block, col[3] *= 0.3f; } - if (!is_icon && typeflags & FILE_TYPE_BLENDERLIB) { + if (!is_icon && file->typeflag & FILE_TYPE_BLENDERLIB) { /* Datablock preview images use premultiplied alpha. */ GPU_blend(GPU_BLEND_ALPHA_PREMULT); } @@ -324,7 +406,7 @@ static void file_draw_preview(uiBlock *block, icon_color[2] = 255; } icon_x = xco + (ex / 2.0f) - (icon_size / 2.0f); - icon_y = yco + (ey / 2.0f) - (icon_size * ((typeflags & FILE_TYPE_DIR) ? 0.78f : 0.75f)); + icon_y = yco + (ey / 2.0f) - (icon_size * ((file->typeflag & FILE_TYPE_DIR) ? 0.78f : 0.75f)); UI_icon_draw_ex( icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false); } @@ -346,13 +428,13 @@ static void file_draw_preview(uiBlock *block, /* Link to folder or non-previewed file. */ uchar icon_color[4]; UI_GetThemeColor4ubv(TH_BACK, icon_color); - icon_x = xco + ((typeflags & FILE_TYPE_DIR) ? 0.14f : 0.23f) * scaledx; - icon_y = yco + ((typeflags & FILE_TYPE_DIR) ? 0.24f : 0.14f) * scaledy; + icon_x = xco + ((file->typeflag & FILE_TYPE_DIR) ? 0.14f : 0.23f) * scaledx; + icon_y = yco + ((file->typeflag & FILE_TYPE_DIR) ? 0.24f : 0.14f) * scaledy; UI_icon_draw_ex( icon_x, icon_y, arrow, icon_aspect / U.dpi_fac * 1.8, 0.3f, 0.0f, icon_color, false); } } - else if (icon && !is_icon && !(typeflags & FILE_TYPE_FTFONT)) { + else if (icon && !is_icon && !(file->typeflag & FILE_TYPE_FTFONT)) { /* Smaller, fainter icon at bottom-left for preview image thumbnail, but not for fonts. */ float icon_x, icon_y; const uchar dark[4] = {0, 0, 0, 255}; @@ -385,8 +467,22 @@ static void file_draw_preview(uiBlock *block, /* dragregion */ if (drag) { + ID *id; + + if ((id = filelist_file_get_id(file))) { + UI_but_drag_set_id(but, id); + } /* path is no more static, cannot give it directly to but... */ - UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true); + else if (file->typeflag & FILE_TYPE_ASSET) { + char blend_path[FILE_MAX_LIBEXTRA]; + if (BLO_library_path_explode(path, blend_path, NULL, NULL)) { + UI_but_drag_set_asset( + but, file->name, BLI_strdup(blend_path), file->blentype, icon, imb, scale); + } + } + else { + UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true); + } } GPU_blend(GPU_BLEND_NONE); @@ -821,6 +917,7 @@ void file_draw_list(const bContext *C, ARegion *region) } file_draw_preview(block, + file, path, sx, sy, @@ -829,13 +926,13 @@ void file_draw_list(const bContext *C, ARegion *region) icon, layout, is_icon, - file->typeflag, do_drag, is_hidden, is_link); } else { file_draw_icon(block, + file, path, sx, sy - layout->tile_border_y, @@ -906,3 +1003,66 @@ void file_draw_list(const bContext *C, ARegion *region) layout->curr_size = params->thumbnail_size; } + +static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion *region) +{ + const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); + + char library_ui_path[PATH_MAX]; + file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path)); + + uchar text_col[4]; + uchar text_alert_col[4]; + UI_GetThemeColor4ubv(TH_TEXT, text_col); + UI_GetThemeColor4ubv(TH_REDALERT, text_alert_col); + + const View2D *v2d = ®ion->v2d; + const int pad = sfile->layout->tile_border_x; + const int width = BLI_rctf_size_x(&v2d->tot) - (2 * pad); + const int line_height = sfile->layout->textheight; + int sx = v2d->tot.xmin + pad; + /* For some reason no padding needed. */ + int sy = v2d->tot.ymax; + + { + const char *message = TIP_("Library not found"); + const int draw_string_str_len = strlen(message) + 2 + sizeof(library_ui_path); + char *draw_string = alloca(draw_string_str_len); + BLI_snprintf(draw_string, draw_string_str_len, "%s: %s", message, library_ui_path); + file_draw_string_multiline(sx, sy, draw_string, width, line_height, text_alert_col, NULL, &sy); + } + + /* Next line, but separate it a bit further. */ + sy -= line_height; + + { + UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO); + + const char *suggestion = TIP_( + "Set up the library or edit libraries in the Preferences, File Paths section."); + file_draw_string_multiline( + sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, NULL); + } +} + +/** + * Draw a string hint if the file list is invalid. + * \return true if the list is invalid and a hint was drawn. + */ +bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) +{ + FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); + /* Only for asset browser. */ + if (!ED_fileselect_is_asset_browser(sfile)) { + return false; + } + /* Check if the library exists. */ + if ((asset_params->asset_library.type == FILE_ASSET_LIBRARY_LOCAL) || + filelist_is_dir(sfile->files, asset_params->base_params.dir)) { + return false; + } + + file_draw_invalid_library_hint(sfile, region); + + return true; +} diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index b459c02d9e5..56fb588776e 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -38,6 +38,7 @@ struct View2D; void file_calc_previews(const bContext *C, ARegion *region); void file_draw_list(const bContext *C, ARegion *region); +bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region); void file_draw_check_ex(bContext *C, struct ScrArea *area); void file_draw_check(bContext *C); @@ -90,6 +91,7 @@ void file_sfile_to_operator(struct Main *bmain, struct wmOperator *op, struct Sp void file_operator_to_sfile(struct Main *bmain, struct SpaceFile *sfile, struct wmOperator *op); /* filesel.c */ +void fileselect_refresh_params(struct SpaceFile *sfile); void fileselect_file_set(SpaceFile *sfile, const int index); bool file_attribute_column_type_enabled(const FileSelectParams *params, FileAttributeColumnType column); @@ -116,3 +118,5 @@ void file_execute_region_panels_register(struct ARegionType *art); /* file_utils.c */ void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds); + +void file_path_to_ui_path(const char *path, char *r_pathi, int max_size); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index b98348307f3..3730174a6c7 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -40,6 +40,7 @@ # include "BLI_winstuff.h" #endif +#include "ED_asset.h" #include "ED_fileselect.h" #include "ED_screen.h" #include "ED_select_utils.h" @@ -930,6 +931,7 @@ void FILE_OT_select_all(wmOperatorType *ot) /* Note we could get rid of this one, but it's used by some addon so... * Does not hurt keeping it around for now. */ +/* TODO disallow bookmark editing in assets mode? */ static int bookmark_select_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -1836,10 +1838,6 @@ static int file_previous_exec(bContext *C, wmOperator *UNUSED(op)) FileSelectParams *params = ED_fileselect_get_active_params(sfile); if (params) { - if (!sfile->folders_next) { - sfile->folders_next = folderlist_new(); - } - folderlist_pushdir(sfile->folders_next, params->dir); folderlist_popdir(sfile->folders_prev, params->dir); folderlist_pushdir(sfile->folders_next, params->dir); @@ -1874,10 +1872,6 @@ static int file_next_exec(bContext *C, wmOperator *UNUSED(unused)) SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_get_active_params(sfile); if (params) { - if (!sfile->folders_next) { - sfile->folders_next = folderlist_new(); - } - folderlist_pushdir(sfile->folders_prev, params->dir); folderlist_popdir(sfile->folders_next, params->dir); @@ -2702,6 +2696,29 @@ static bool file_delete_poll(bContext *C) return poll; } +static bool file_delete_single(const FileSelectParams *params, + FileDirEntry *file, + const char **r_error_message) +{ + if (file->typeflag & FILE_TYPE_ASSET) { + ID *id = filelist_file_get_id(file); + if (!id) { + *r_error_message = "File is not a local data-block asset."; + return false; + } + ED_asset_clear_id(id); + } + else { + char str[FILE_MAX]; + BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath); + if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) { + return false; + } + } + + return true; +} + static int file_delete_exec(bContext *C, wmOperator *op) { wmWindowManager *wm = CTX_wm_manager(C); @@ -2715,9 +2732,7 @@ static int file_delete_exec(bContext *C, wmOperator *op) for (int i = 0; i < numfiles; i++) { if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) { FileDirEntry *file = filelist_file(sfile->files, i); - char str[FILE_MAX]; - BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath); - if (BLI_delete_soft(str, &error_message) != 0 || BLI_exists(str)) { + if (!file_delete_single(params, file, &error_message)) { report_error = true; } } @@ -2763,13 +2778,20 @@ void FILE_OT_delete(struct wmOperatorType *ot) static int file_start_filter_exec(bContext *C, wmOperator *UNUSED(op)) { ScrArea *area = CTX_wm_area(C); - ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_UI); SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_get_active_params(sfile); ARegion *region_ctx = CTX_wm_region(C); - CTX_wm_region_set(C, region); - UI_textbutton_activate_rna(C, region, params, "filter_search"); + + if (area) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + CTX_wm_region_set(C, region); + if (UI_textbutton_activate_rna(C, region, params, "filter_search")) { + break; + } + } + } + CTX_wm_region_set(C, region_ctx); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_file/file_utils.c b/source/blender/editors/space_file/file_utils.c index 452f2f704cf..9d85996c559 100644 --- a/source/blender/editors/space_file/file_utils.c +++ b/source/blender/editors/space_file/file_utils.c @@ -18,12 +18,14 @@ * \ingroup spfile */ +#include "BLI_fileops.h" #include "BLI_listbase.h" +#include "BLI_path_util.h" #include "BLI_rect.h" - -#include "BLO_readfile.h" +#include "BLI_string.h" #include "BKE_context.h" +#include "BLO_readfile.h" #include "ED_fileselect.h" #include "ED_screen.h" @@ -44,3 +46,14 @@ void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int fil ymax - layout->tile_h - layout->tile_border_y, ymax); } + +/** + * If \a path leads to a .blend, remove the trailing slash (if needed). + */ +void file_path_to_ui_path(const char *path, char *r_path, int max_size) +{ + char tmp_path[PATH_MAX]; + BLI_strncpy(tmp_path, path, sizeof(tmp_path)); + BLI_path_slash_rstrip(tmp_path); + BLI_strncpy(r_path, BLO_has_bfile_extension(tmp_path) ? tmp_path : path, max_size); +} diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index e87142a7096..d66219c7549 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -53,13 +53,18 @@ # include "BLI_winstuff.h" #endif +#include "BKE_asset.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_idtype.h" +#include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_main_idmap.h" +#include "BKE_preferences.h" #include "BLO_readfile.h" +#include "DNA_asset_types.h" #include "DNA_space_types.h" #include "ED_datafiles.h" @@ -82,6 +87,8 @@ #include "filelist.h" +#define FILEDIR_NBR_ENTRIES_UNSET -1 + /* ----------------- FOLDERLIST (previous/next) -------------- */ typedef struct FolderList { @@ -89,12 +96,6 @@ typedef struct FolderList { char *foldername; } FolderList; -ListBase *folderlist_new(void) -{ - ListBase *p = MEM_callocN(sizeof(*p), __func__); - return p; -} - void folderlist_popdir(struct ListBase *folderlist, char *dir) { const char *prev_dir; @@ -117,6 +118,10 @@ void folderlist_popdir(struct ListBase *folderlist, char *dir) void folderlist_pushdir(ListBase *folderlist, const char *dir) { + if (!dir[0]) { + return; + } + struct FolderList *folder, *previous_folder; previous_folder = folderlist->last; @@ -153,7 +158,7 @@ int folderlist_clear_next(struct SpaceFile *sfile) struct FolderList *folder; /* if there is no folder_next there is nothing we can clear */ - if (!sfile->folders_next) { + if (BLI_listbase_is_empty(sfile->folders_next)) { return 0; } @@ -180,23 +185,79 @@ void folderlist_free(ListBase *folderlist) } } -ListBase *folderlist_duplicate(ListBase *folderlist) +static ListBase folderlist_duplicate(ListBase *folderlist) { + ListBase folderlistn = {NULL}; - if (folderlist) { - ListBase *folderlistn = MEM_callocN(sizeof(*folderlistn), __func__); - FolderList *folder; + BLI_duplicatelist(&folderlistn, folderlist); + + for (FolderList *folder = folderlistn.first; folder; folder = folder->next) { + folder->foldername = MEM_dupallocN(folder->foldername); + } + return folderlistn; +} - BLI_duplicatelist(folderlistn, folderlist); +/* ----------------- Folder-History (wraps/owns file list above) -------------- */ - for (folder = folderlistn->first; folder; folder = folder->next) { - folder->foldername = MEM_dupallocN(folder->foldername); +static FileFolderHistory *folder_history_find(const SpaceFile *sfile, eFileBrowse_Mode browse_mode) +{ + LISTBASE_FOREACH (FileFolderHistory *, history, &sfile->folder_histories) { + if (history->browse_mode == browse_mode) { + return history; } - return folderlistn; } + return NULL; } +void folder_history_list_ensure_for_active_browse_mode(SpaceFile *sfile) +{ + FileFolderHistory *history = folder_history_find(sfile, sfile->browse_mode); + + if (!history) { + history = MEM_callocN(sizeof(*history), __func__); + history->browse_mode = sfile->browse_mode; + BLI_addtail(&sfile->folder_histories, history); + } + + sfile->folders_next = &history->folders_next; + sfile->folders_prev = &history->folders_prev; +} + +static void folder_history_entry_free(SpaceFile *sfile, FileFolderHistory *history) +{ + if (sfile->folders_prev == &history->folders_prev) { + sfile->folders_prev = NULL; + } + if (sfile->folders_next == &history->folders_next) { + sfile->folders_next = NULL; + } + folderlist_free(&history->folders_prev); + folderlist_free(&history->folders_next); + BLI_freelinkN(&sfile->folder_histories, history); +} + +void folder_history_list_free(SpaceFile *sfile) +{ + LISTBASE_FOREACH_MUTABLE (FileFolderHistory *, history, &sfile->folder_histories) { + folder_history_entry_free(sfile, history); + } +} + +ListBase folder_history_list_duplicate(ListBase *listbase) +{ + ListBase histories = {NULL}; + + LISTBASE_FOREACH (FileFolderHistory *, history, listbase) { + FileFolderHistory *history_new = MEM_dupallocN(history); + history_new->folders_prev = folderlist_duplicate(&history->folders_prev); + history_new->folders_next = folderlist_duplicate(&history->folders_next); + BLI_addtail(&histories, history_new); + } + + return histories; +} + /* ------------------FILELIST------------------------ */ typedef struct FileListInternEntry { @@ -216,6 +277,24 @@ typedef struct FileListInternEntry { /** not strictly needed, but used during sorting, avoids to have to recompute it there... */ char *name; + /** + * This is data from the current main, represented by this file. It's crucial that this is + * updated correctly on undo, redo and file reading (without UI). The space is responsible to + * take care of that. + */ + struct { + /** When showing local IDs (FILE_MAIN, FILE_MAIN_ASSET), the ID this file entry represents. */ + ID *id; + + /* For the few file types that have the preview already in memory. For others, there's delayed + * preview reading from disk. Non-owning pointer. */ + PreviewImage *preview_image; + } local_data; + + /** When the file represents an asset read from another file, it is stored here. + * Owning pointer. */ + AssetMetaData *imported_asset_data; + /** Defined in BLI_fileops.h */ eFileAttributes attributes; BLI_stat_t st; @@ -267,7 +346,11 @@ typedef struct FileListEntryPreview { char path[FILE_MAX]; uint flags; int index; - ImBuf *img; + /* Some file types load the memory from runtime data, not from disk. We just wait until it's done + * generating (BKE_previewimg_is_finished()). */ + PreviewImage *in_memory_preview; + + int icon_id; } FileListEntryPreview; /* Dummy wrapper around FileListEntryPreview to ensure we do not access freed memory when freeing @@ -290,11 +373,16 @@ enum { FLF_HIDE_DOT = 1 << 1, FLF_HIDE_PARENT = 1 << 2, FLF_HIDE_LIB_DIR = 1 << 3, + FLF_ASSETS_ONLY = 1 << 4, }; typedef struct FileList { FileDirEntryArr filelist; + eFileSelectType type; + /* The library this list was created for. Stored here so we know when to re-read. */ + FileSelectAssetLibraryUID *asset_library; + short flags; short sort; @@ -324,10 +412,13 @@ typedef struct FileList { bool (*checkdirf)(struct FileList *, char *, const bool); /* Fill filelist (to be called by read job). */ - void (*read_jobf)(struct FileList *, const char *, short *, short *, float *, ThreadMutex *); + void (*read_jobf)( + Main *, struct FileList *, const char *, short *, short *, float *, ThreadMutex *); /* Filter an entry of current filelist. */ bool (*filterf)(struct FileListInternEntry *, const char *, FileListFilter *); + + short tags; /* FileListTags */ } FileList; /* FileList.flags */ @@ -340,6 +431,14 @@ enum { FL_SORT_INVERT = 1 << 5, }; +/* FileList.tags */ +enum FileListTags { + /** The file list has references to main data (IDs) and needs special care. */ + FILELIST_TAGS_USES_MAIN_DATA = (1 << 0), + /** The file list type is not thread-safe. */ + FILELIST_TAGS_NO_THREADS = (1 << 2), +}; + #define SPECIAL_IMG_SIZE 256 #define SPECIAL_IMG_ROWS 1 #define SPECIAL_IMG_COLS 7 @@ -357,24 +456,34 @@ enum { static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX]; -static void filelist_readjob_main(FileList *filelist, +static void filelist_readjob_main(Main *current_main, + FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock); -static void filelist_readjob_lib(FileList *filelist, +static void filelist_readjob_lib(Main *current_main, + FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock); -static void filelist_readjob_dir(FileList *filelist, +static void filelist_readjob_dir(Main *current_main, + FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock); +static void filelist_readjob_main_assets(Main *current_main, + FileList *filelist, + const char *main_name, + short *stop, + short *do_update, + float *progress, + ThreadMutex *lock); /* helper, could probably go in BKE actually? */ static int groupname_to_code(const char *group); @@ -694,6 +803,11 @@ static bool is_filtered_hidden(const char *filename, return true; } #endif + /* For data-blocks (but not the group directories), check the asset-only filter. */ + if (!(file->typeflag & FILE_TYPE_DIR) && (file->typeflag & FILE_TYPE_BLENDERLIB) && + (filter->flags & FLF_ASSETS_ONLY) && !(file->typeflag & FILE_TYPE_ASSET)) { + return true; + } return false; } @@ -737,51 +851,61 @@ static bool is_filtered_file(FileListInternEntry *file, return is_filtered; } -static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) +static bool is_filtered_id_file(const FileListInternEntry *file, + const char *id_group, + const char *name, + const FileListFilter *filter) { - bool is_filtered; - char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name; - - BLI_join_dirfile(path, sizeof(path), root, file->relpath); - - if (BLO_library_path_explode(path, dir, &group, &name)) { - is_filtered = !is_filtered_hidden(file->relpath, filter, file); - if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { - /* We only check for types if some type are enabled in filtering. */ - if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) { - if (file->typeflag & FILE_TYPE_DIR) { - if (file->typeflag & - (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { - if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { - is_filtered = false; - } - } - else { - if (!(filter->filter & FILE_TYPE_FOLDER)) { - is_filtered = false; - } + bool is_filtered = !is_filtered_hidden(file->relpath, filter, file); + if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { + /* We only check for types if some type are enabled in filtering. */ + if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) { + if (file->typeflag & FILE_TYPE_DIR) { + if (file->typeflag & + (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { + if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { + is_filtered = false; } } - if (is_filtered && group) { - if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) { + else { + if (!(filter->filter & FILE_TYPE_FOLDER)) { is_filtered = false; } - else { - uint64_t filter_id = groupname_to_filter_id(group); - if (!(filter_id & filter->filter_id)) { - is_filtered = false; - } - } } } - /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ - if (is_filtered && (filter->filter_search[0] != '\0')) { - if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { + if (is_filtered && id_group) { + if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) { is_filtered = false; } + else { + uint64_t filter_id = groupname_to_filter_id(id_group); + if (!(filter_id & filter->filter_id)) { + is_filtered = false; + } + } + } + } + /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ + if (is_filtered && (filter->filter_search[0] != '\0')) { + if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { + is_filtered = false; } } } + + return is_filtered; +} + +static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) +{ + bool is_filtered; + char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name; + + BLI_join_dirfile(path, sizeof(path), root, file->relpath); + + if (BLO_library_path_explode(path, dir, &group, &name)) { + is_filtered = is_filtered_id_file(file, group, name, filter); + } else { is_filtered = is_filtered_file(file, root, filter); } @@ -796,6 +920,14 @@ static bool is_filtered_main(FileListInternEntry *file, return !is_filtered_hidden(file->relpath, filter, file); } +static bool is_filtered_main_assets(FileListInternEntry *file, + const char *UNUSED(dir), + FileListFilter *filter) +{ + /* "Filtered" means *not* being filtered out... So return true if the file should be visible. */ + return is_filtered_id_file(file, file->relpath, file->name, filter); +} + static void filelist_filter_clear(FileList *filelist) { filelist->flags |= FL_NEED_FILTERING; @@ -807,7 +939,7 @@ void filelist_filter(FileList *filelist) const int num_files = filelist->filelist.nbr_entries; FileListInternEntry **filtered_tmp, *file; - if (filelist->filelist.nbr_entries == 0) { + if (ELEM(filelist->filelist.nbr_entries, FILEDIR_NBR_ENTRIES_UNSET, 0)) { return; } @@ -858,6 +990,7 @@ void filelist_setfilter_options(FileList *filelist, const bool hide_parent, const uint64_t filter, const uint64_t filter_id, + const bool filter_assets_only, const char *filter_glob, const char *filter_search) { @@ -875,6 +1008,10 @@ void filelist_setfilter_options(FileList *filelist, filelist->filter_data.flags ^= FLF_HIDE_PARENT; update = true; } + if (((filelist->filter_data.flags & FLF_ASSETS_ONLY) != 0) != (filter_assets_only != 0)) { + filelist->filter_data.flags ^= FLF_ASSETS_ONLY; + update = true; + } if (filelist->filter_data.filter != filter) { filelist->filter_data.filter = filter; update = true; @@ -903,6 +1040,54 @@ void filelist_setfilter_options(FileList *filelist, } } +/** + * Checks two libraries for equality. + * \return True if the libraries match. + */ +static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *library_a, + const FileSelectAssetLibraryUID *library_b) +{ + if (library_a->type != library_b->type) { + return false; + } + if (library_a->type == FILE_ASSET_LIBRARY_CUSTOM) { + /* Don't only check the index, also check that it's valid. */ + bUserAssetLibrary *library_ptr_a = BKE_preferences_asset_library_find_from_index( + &U, library_a->custom_library_index); + return (library_ptr_a != NULL) && + (library_a->custom_library_index == library_b->custom_library_index); + } + + return true; +} + +/** + * \param asset_library: May be NULL to unset the library. + */ +void filelist_setlibrary(FileList *filelist, const FileSelectAssetLibraryUID *asset_library) +{ + /* Unset if needed. */ + if (!asset_library) { + if (filelist->asset_library) { + MEM_SAFE_FREE(filelist->asset_library); + filelist->flags |= FL_FORCE_RESET; + } + return; + } + + if (!filelist->asset_library) { + filelist->asset_library = MEM_mallocN(sizeof(*filelist->asset_library), + "filelist asset library"); + *filelist->asset_library = *asset_library; + + filelist->flags |= FL_FORCE_RESET; + } + else if (!filelist_compare_asset_libraries(filelist->asset_library, asset_library)) { + *filelist->asset_library = *asset_library; + filelist->flags |= FL_FORCE_RESET; + } +} + /* ********** Icon/image helpers ********** */ void filelist_init_icons(void) @@ -960,7 +1145,12 @@ ImBuf *filelist_getimage(struct FileList *filelist, const int index) { FileDirEntry *file = filelist_geticon_get_file(filelist, index); - return file->image; + return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL; +} + +ImBuf *filelist_file_getimage(const FileDirEntry *file) +{ + return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL; } static ImBuf *filelist_geticon_image_ex(FileDirEntry *file) @@ -988,12 +1178,12 @@ ImBuf *filelist_geticon_image(struct FileList *filelist, const int index) return filelist_geticon_image_ex(file); } -static int filelist_geticon_ex(FileDirEntry *file, +static int filelist_geticon_ex(const FileDirEntry *file, const char *root, const bool is_main, const bool ignore_libdir) { - const int typeflag = file->typeflag; + const eFileSel_File_Types typeflag = file->typeflag; if ((typeflag & FILE_TYPE_DIR) && !(ignore_libdir && (typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER)))) { @@ -1027,7 +1217,7 @@ static int filelist_geticon_ex(FileDirEntry *file, if (file->redirection_path) { target = file->redirection_path; } - else { + else if (root) { BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath); BLI_path_slash_ensure(fullpath); } @@ -1111,6 +1301,12 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m return filelist_geticon_ex(file, filelist->filelist.root, is_main, false); } +int ED_file_icon(const FileDirEntry *file) +{ + return file->preview_icon_id ? file->preview_icon_id : + filelist_geticon_ex(file, NULL, false, false); +} + /* ********** Main ********** */ static void parent_dir_until_exists_or_default_root(char *dir) @@ -1160,6 +1356,14 @@ static bool filelist_checkdir_main(struct FileList *filelist, char *r_dir, const return filelist_checkdir_lib(filelist, r_dir, do_change); } +static bool filelist_checkdir_main_assets(struct FileList *UNUSED(filelist), + char *UNUSED(r_dir), + const bool UNUSED(do_change)) +{ + /* Main is always valid. */ + return true; +} + static void filelist_entry_clear(FileDirEntry *entry) { if (entry->name) { @@ -1174,8 +1378,9 @@ static void filelist_entry_clear(FileDirEntry *entry) if (entry->redirection_path) { MEM_freeN(entry->redirection_path); } - if (entry->image) { - IMB_freeImBuf(entry->image); + if (entry->preview_icon_id) { + BKE_icon_delete(entry->preview_icon_id); + entry->preview_icon_id = 0; } /* For now, consider FileDirEntryRevision::poin as not owned here, * so no need to do anything about it */ @@ -1232,8 +1437,8 @@ static void filelist_direntryarr_free(FileDirEntryArr *array) #else BLI_assert(BLI_listbase_is_empty(&array->entries)); #endif - array->nbr_entries = 0; - array->nbr_entries_filtered = -1; + array->nbr_entries = FILEDIR_NBR_ENTRIES_UNSET; + array->nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET; array->entry_idx_start = -1; array->entry_idx_end = -1; } @@ -1249,6 +1454,10 @@ static void filelist_intern_entry_free(FileListInternEntry *entry) if (entry->name) { MEM_freeN(entry->name); } + /* If we own the asset-data (it was generated from external file data), free it. */ + if (entry->imported_asset_data) { + BKE_asset_metadata_free(&entry->imported_asset_data); + } MEM_freeN(entry); } @@ -1272,37 +1481,54 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat FileListEntryPreview *preview = preview_taskdata->preview; ThumbSource source = 0; + bool done = false; // printf("%s: Start (%d)...\n", __func__, threadid); - // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); - BLI_assert(preview->flags & - (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER | - FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)); - - if (preview->flags & FILE_TYPE_IMAGE) { - source = THB_SOURCE_IMAGE; - } - else if (preview->flags & - (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) { - source = THB_SOURCE_BLEND; - } - else if (preview->flags & FILE_TYPE_MOVIE) { - source = THB_SOURCE_MOVIE; - } - else if (preview->flags & FILE_TYPE_FTFONT) { - source = THB_SOURCE_FONT; + if (preview->in_memory_preview) { + if (BKE_previewimg_is_finished(preview->in_memory_preview, ICON_SIZE_PREVIEW)) { + ImBuf *imbuf = BKE_previewimg_to_imbuf(preview->in_memory_preview, ICON_SIZE_PREVIEW); + preview->icon_id = BKE_icon_imbuf_create(imbuf); + done = true; + } } + else { + // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + BLI_assert(preview->flags & + (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER | + FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)); - IMB_thumb_path_lock(preview->path); - /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate in - * case user switch to a bigger preview size. */ - preview->img = IMB_thumb_manage(preview->path, THB_LARGE, source); - IMB_thumb_path_unlock(preview->path); + if (preview->flags & FILE_TYPE_IMAGE) { + source = THB_SOURCE_IMAGE; + } + else if (preview->flags & + (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) { + source = THB_SOURCE_BLEND; + } + else if (preview->flags & FILE_TYPE_MOVIE) { + source = THB_SOURCE_MOVIE; + } + else if (preview->flags & FILE_TYPE_FTFONT) { + source = THB_SOURCE_FONT; + } + + IMB_thumb_path_lock(preview->path); + /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate + * in case user switch to a bigger preview size. */ + ImBuf *imbuf = IMB_thumb_manage(preview->path, THB_LARGE, source); + IMB_thumb_path_unlock(preview->path); + if (imbuf) { + preview->icon_id = BKE_icon_imbuf_create(imbuf); + } + + done = true; + } - /* That way task freeing function won't free th preview, since it does not own it anymore. */ - atomic_cas_ptr((void **)&preview_taskdata->preview, preview, NULL); - BLI_thread_queue_push(cache->previews_done, preview); + if (done) { + /* That way task freeing function won't free th preview, since it does not own it anymore. */ + atomic_cas_ptr((void **)&preview_taskdata->preview, preview, NULL); + BLI_thread_queue_push(cache->previews_done, preview); + } // printf("%s: End (%d)...\n", __func__, threadid); } @@ -1315,8 +1541,8 @@ static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void /* preview_taskdata->preview is atomically set to NULL once preview has been processed and sent * to previews_done queue. */ if (preview != NULL) { - if (preview->img) { - IMB_freeImBuf(preview->img); + if (preview->icon_id) { + BKE_icon_delete(preview->icon_id); } MEM_freeN(preview); } @@ -1342,8 +1568,8 @@ static void filelist_cache_previews_clear(FileListEntryCache *cache) while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) { // printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path, // preview->img); - if (preview->img) { - IMB_freeImBuf(preview->img); + if (preview->icon_id) { + BKE_icon_delete(preview->icon_id); } MEM_freeN(preview); } @@ -1374,10 +1600,11 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE); - if (!entry->image && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) && + if (!entry->preview_icon_id && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) && (entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) { FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__); + FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index]; if (entry->redirection_path) { BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR); @@ -1389,7 +1616,8 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry preview->index = index; preview->flags = entry->typeflag; - preview->img = NULL; + preview->in_memory_preview = intern_entry->local_data.preview_image; + preview->icon_id = 0; // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); filelist_cache_preview_ensure_running(cache); @@ -1497,25 +1725,45 @@ FileList *filelist_new(short type) p->selection_state = BLI_ghash_new( BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__); + p->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET; + filelist_settype(p, type); - switch (type) { + return p; +} + +void filelist_settype(FileList *filelist, short type) +{ + if (filelist->type == type) { + return; + } + + filelist->type = type; + filelist->tags = 0; + switch (filelist->type) { case FILE_MAIN: - p->checkdirf = filelist_checkdir_main; - p->read_jobf = filelist_readjob_main; - p->filterf = is_filtered_main; + filelist->checkdirf = filelist_checkdir_main; + filelist->read_jobf = filelist_readjob_main; + filelist->filterf = is_filtered_main; break; case FILE_LOADLIB: - p->checkdirf = filelist_checkdir_lib; - p->read_jobf = filelist_readjob_lib; - p->filterf = is_filtered_lib; + filelist->checkdirf = filelist_checkdir_lib; + filelist->read_jobf = filelist_readjob_lib; + filelist->filterf = is_filtered_lib; + break; + case FILE_MAIN_ASSET: + filelist->checkdirf = filelist_checkdir_main_assets; + filelist->read_jobf = filelist_readjob_main_assets; + filelist->filterf = is_filtered_main_assets; + filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA | FILELIST_TAGS_NO_THREADS; break; default: - p->checkdirf = filelist_checkdir_dir; - p->read_jobf = filelist_readjob_dir; - p->filterf = is_filtered_file; + filelist->checkdirf = filelist_checkdir_dir; + filelist->read_jobf = filelist_readjob_dir; + filelist->filterf = is_filtered_file; break; } - return p; + + filelist->flags |= FL_FORCE_RESET; } void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection) @@ -1560,6 +1808,8 @@ void filelist_free(struct FileList *filelist) filelist->selection_state = NULL; } + MEM_SAFE_FREE(filelist->asset_library); + memset(&filelist->filter_data, 0, sizeof(filelist->filter_data)); filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING); @@ -1580,7 +1830,7 @@ BlendHandle *filelist_lib(struct FileList *filelist) static const char *fileentry_uiname(const char *root, const char *relpath, - const int typeflag, + const eFileSel_File_Types typeflag, char *buff) { char *name = NULL; @@ -1624,11 +1874,12 @@ bool filelist_is_dir(struct FileList *filelist, const char *path) */ void filelist_setdir(struct FileList *filelist, char *r_dir) { + const bool allow_invalid = filelist->asset_library != NULL; BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA); BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir); - const bool is_valid_path = filelist->checkdirf(filelist, r_dir, true); - BLI_assert(is_valid_path); + const bool is_valid_path = filelist->checkdirf(filelist, r_dir, !allow_invalid); + BLI_assert(is_valid_path || allow_invalid); UNUSED_VARS_NDEBUG(is_valid_path); if (!STREQ(filelist->filelist.root, r_dir)) { @@ -1645,11 +1896,16 @@ void filelist_setrecursion(struct FileList *filelist, const int recursion_level) } } -bool filelist_force_reset(struct FileList *filelist) +bool filelist_needs_force_reset(FileList *filelist) { return (filelist->flags & FL_FORCE_RESET) != 0; } +void filelist_tag_force_reset(FileList *filelist) +{ + filelist->flags |= FL_FORCE_RESET; +} + bool filelist_is_ready(struct FileList *filelist) { return (filelist->flags & FL_IS_READY) != 0; @@ -1660,6 +1916,11 @@ bool filelist_pending(struct FileList *filelist) return (filelist->flags & FL_IS_PENDING) != 0; } +bool filelist_needs_reset_on_main_changes(const FileList *filelist) +{ + return (filelist->tags & FILELIST_TAGS_USES_MAIN_DATA) != 0; +} + /** * Limited version of full update done by space_file's file_refresh(), * to be used by operators and such. @@ -1668,7 +1929,7 @@ bool filelist_pending(struct FileList *filelist) */ int filelist_files_ensure(FileList *filelist) { - if (!filelist_force_reset(filelist) || !filelist_empty(filelist)) { + if (!filelist_needs_force_reset(filelist) || !filelist_needs_reading(filelist)) { filelist_sort(filelist); filelist_filter(filelist); } @@ -1701,6 +1962,17 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in if (entry->redirection_path) { ret->redirection_path = BLI_strdup(entry->redirection_path); } + ret->id = entry->local_data.id; + ret->asset_data = entry->imported_asset_data ? entry->imported_asset_data : NULL; + if (ret->id && (ret->asset_data == NULL)) { + ret->asset_data = ret->id->asset_data; + } + /* For some file types the preview is already available. */ + if (entry->local_data.preview_image && + BKE_previewimg_is_finished(entry->local_data.preview_image, ICON_SIZE_PREVIEW)) { + ImBuf *ibuf = BKE_previewimg_to_imbuf(entry->local_data.preview_image, ICON_SIZE_PREVIEW); + ret->preview_icon_id = BKE_icon_imbuf_create(ibuf); + } BLI_addtail(&cache->cached_entries, ret); return ret; } @@ -1770,7 +2042,7 @@ int filelist_file_findpath(struct FileList *filelist, const char *filename) { int fidx = -1; - if (filelist->filelist.nbr_entries_filtered < 0) { + if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) { return fidx; } @@ -1788,9 +2060,17 @@ int filelist_file_findpath(struct FileList *filelist, const char *filename) return -1; } +/** + * Get the ID a file represents (if any). For #FILE_MAIN, #FILE_MAIN_ASSET. + */ +ID *filelist_file_get_id(const FileDirEntry *file) +{ + return file->id; +} + FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4]) { - if (filelist->filelist.nbr_entries_filtered < 0) { + if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) { return NULL; } @@ -2147,15 +2427,17 @@ bool filelist_cache_previews_update(FileList *filelist) // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); - if (preview->img) { + if (preview->icon_id) { /* Due to asynchronous process, a preview for a given image may be generated several times, * i.e. entry->image may already be set at this point. */ - if (entry && !entry->image) { - entry->image = preview->img; + if (entry && !entry->preview_icon_id) { + /* Move ownership over icon. */ + entry->preview_icon_id = preview->icon_id; + preview->icon_id = 0; changed = true; } else { - IMB_freeImBuf(preview->img); + BKE_icon_delete(preview->icon_id); } } else if (entry) { @@ -2313,9 +2595,9 @@ int ED_file_extension_icon(const char *path) } } -int filelist_empty(struct FileList *filelist) +int filelist_needs_reading(struct FileList *filelist) { - return (filelist->filelist.nbr_entries == 0); + return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET); } uint filelist_entry_select_set(const FileList *filelist, @@ -2564,8 +2846,8 @@ static int filelist_readjob_list_dir(const char *root, static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar) { FileListInternEntry *entry; - LinkNode *ln, *names; - int i, nnames, idcode = 0, nbr_entries = 0; + LinkNode *ln, *names = NULL, *datablock_infos = NULL; + int i, nitems, idcode = 0, nbr_entries = 0; char dir[FILE_MAX_LIBEXTRA], *group; bool ok; @@ -2587,11 +2869,11 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const * and freed in filelist_entry_free. */ if (group) { idcode = groupname_to_code(group); - names = BLO_blendhandle_get_datablock_names(libfiledata, idcode, &nnames); + datablock_infos = BLO_blendhandle_get_datablock_info(libfiledata, idcode, &nitems); } else { names = BLO_blendhandle_get_linkable_groups(libfiledata); - nnames = BLI_linklist_count(names); + nitems = BLI_linklist_count(names); } BLO_blendhandle_close(libfiledata); @@ -2604,12 +2886,18 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const nbr_entries++; } - for (i = 0, ln = names; i < nnames; i++, ln = ln->next) { - const char *blockname = ln->link; + for (i = 0, ln = (datablock_infos ? datablock_infos : names); i < nitems; i++, ln = ln->next) { + struct BLODataBlockInfo *info = datablock_infos ? ln->link : NULL; + const char *blockname = info ? info->name : ln->link; entry = MEM_callocN(sizeof(*entry), __func__); entry->relpath = BLI_strdup(blockname); entry->typeflag |= FILE_TYPE_BLENDERLIB; + if (info && info->asset_data) { + entry->typeflag |= FILE_TYPE_ASSET; + /* Moves ownership! */ + entry->imported_asset_data = info->asset_data; + } if (!(group && idcode)) { entry->typeflag |= FILE_TYPE_DIR; entry->blentype = groupname_to_code(blockname); @@ -2621,7 +2909,7 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const nbr_entries++; } - BLI_linklist_freeN(names); + BLI_linklist_freeN(datablock_infos ? datablock_infos : names); return nbr_entries; } @@ -2820,7 +3108,10 @@ static void filelist_readjob_do(const bool do_lib, // BLI_assert(filelist->filtered == NULL); BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && - (filelist->filelist.nbr_entries == 0)); + (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + + /* A valid, but empty directory from now. */ + filelist->filelist.nbr_entries = 0; todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__); td_dir = BLI_stack_push_r(todo_dirs); @@ -2932,7 +3223,8 @@ static void filelist_readjob_do(const bool do_lib, BLI_stack_free(todo_dirs); } -static void filelist_readjob_dir(FileList *filelist, +static void filelist_readjob_dir(Main *UNUSED(current_main), + FileList *filelist, const char *main_name, short *stop, short *do_update, @@ -2942,7 +3234,8 @@ static void filelist_readjob_dir(FileList *filelist, filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock); } -static void filelist_readjob_lib(FileList *filelist, +static void filelist_readjob_lib(Main *UNUSED(current_main), + FileList *filelist, const char *main_name, short *stop, short *do_update, @@ -2952,7 +3245,8 @@ static void filelist_readjob_lib(FileList *filelist, filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock); } -static void filelist_readjob_main(FileList *filelist, +static void filelist_readjob_main(Main *current_main, + FileList *filelist, const char *main_name, short *stop, short *do_update, @@ -2960,12 +3254,67 @@ static void filelist_readjob_main(FileList *filelist, ThreadMutex *lock) { /* TODO! */ - filelist_readjob_dir(filelist, main_name, stop, do_update, progress, lock); + filelist_readjob_dir(current_main, filelist, main_name, stop, do_update, progress, lock); +} + +/** + * \warning Acts on main, so NOT thread-safe! + */ +static void filelist_readjob_main_assets(Main *current_main, + FileList *filelist, + const char *UNUSED(main_name), + short *UNUSED(stop), + short *do_update, + float *UNUSED(progress), + ThreadMutex *UNUSED(lock)) +{ + BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && + (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + + /* A valid, but empty directory from now. */ + filelist->filelist.nbr_entries = 0; + + FileListInternEntry *entry; + ListBase tmp_entries = {0}; + ID *id_iter; + int nbr_entries = 0; + + FOREACH_MAIN_ID_BEGIN (current_main, id_iter) { + if (!id_iter->asset_data) { + continue; + } + + const char *id_code_name = BKE_idtype_idcode_to_name(GS(id_iter->name)); + + entry = MEM_callocN(sizeof(*entry), __func__); + entry->relpath = BLI_strdup(id_code_name); + entry->name = BLI_strdup(id_iter->name + 2); + entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_ASSET; + entry->blentype = GS(id_iter->name); + *((uint32_t *)entry->uuid) = atomic_add_and_fetch_uint32( + (uint32_t *)filelist->filelist_intern.curr_uuid, 1); + entry->local_data.preview_image = BKE_asset_metadata_preview_get_from_id(id_iter->asset_data, + id_iter); + entry->local_data.id = id_iter; + nbr_entries++; + BLI_addtail(&tmp_entries, entry); + } + FOREACH_MAIN_ID_END; + + if (nbr_entries) { + *do_update = true; + + BLI_movelisttolist(&filelist->filelist.entries, &tmp_entries); + filelist->filelist.nbr_entries += nbr_entries; + filelist->filelist.nbr_entries_filtered = filelist->filelist.entry_idx_start = + filelist->filelist.entry_idx_end = -1; + } } typedef struct FileListReadJob { ThreadMutex lock; char main_name[FILE_MAX]; + Main *current_main; struct FileList *filelist; /** XXX We may use a simpler struct here... just a linked list and root path? */ struct FileList *tmp_filelist; @@ -2985,7 +3334,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update flrj->tmp_filelist = MEM_dupallocN(flrj->filelist); BLI_listbase_clear(&flrj->tmp_filelist->filelist.entries); - flrj->tmp_filelist->filelist.nbr_entries = 0; + flrj->tmp_filelist->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET; flrj->tmp_filelist->filelist_intern.filtered = NULL; BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries); @@ -2996,11 +3345,17 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update flrj->tmp_filelist->libfiledata = NULL; memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache)); flrj->tmp_filelist->selection_state = NULL; + flrj->tmp_filelist->asset_library = NULL; BLI_mutex_unlock(&flrj->lock); - flrj->tmp_filelist->read_jobf( - flrj->tmp_filelist, flrj->main_name, stop, do_update, progress, &flrj->lock); + flrj->tmp_filelist->read_jobf(flrj->current_main, + flrj->tmp_filelist, + flrj->main_name, + stop, + do_update, + progress, + &flrj->lock); } static void filelist_readjob_update(void *flrjv) @@ -3015,7 +3370,7 @@ static void filelist_readjob_update(void *flrjv) BLI_mutex_lock(&flrj->lock); - if (flrj->tmp_filelist->filelist.nbr_entries) { + if (flrj->tmp_filelist->filelist.nbr_entries > 0) { /* We just move everything out of 'thread context' into final list. */ new_nbr_entries = flrj->tmp_filelist->filelist.nbr_entries; BLI_movelisttolist(&new_entries, &flrj->tmp_filelist->filelist.entries); @@ -3033,7 +3388,7 @@ static void filelist_readjob_update(void *flrjv) /* if no new_nbr_entries, this is NOP */ BLI_movelisttolist(&fl_intern->entries, &new_entries); - flrj->filelist->filelist.nbr_entries = nbr_entries + new_nbr_entries; + flrj->filelist->filelist.nbr_entries = MAX2(nbr_entries, 0) + new_nbr_entries; } static void filelist_readjob_endjob(void *flrjv) @@ -3074,16 +3429,36 @@ void filelist_readjob_start(FileList *filelist, const bContext *C) wmJob *wm_job; FileListReadJob *flrj; + if (!filelist_is_dir(filelist, filelist->filelist.root)) { + return; + } + /* prepare job data */ flrj = MEM_callocN(sizeof(*flrj), __func__); flrj->filelist = filelist; + flrj->current_main = bmain; BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name)); filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY); filelist->flags |= FL_IS_PENDING; + /* Init even for single threaded execution. Called functions use it. */ BLI_mutex_init(&flrj->lock); + if (filelist->tags & FILELIST_TAGS_NO_THREADS) { + short dummy_stop = false; + short dummy_do_update = false; + float dummy_progress = 0.0f; + + /* Single threaded execution. Just directly call the callbacks. */ + filelist_readjob_startjob(flrj, &dummy_stop, &dummy_do_update, &dummy_progress); + filelist_readjob_endjob(flrj); + filelist_readjob_free(flrj); + + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); + return; + } + /* setup job */ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 4b8cc052382..16984bb6e43 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -29,6 +29,7 @@ extern "C" { struct BlendHandle; struct FileList; +struct FileSelectAssetLibraryUID; struct FileSelection; struct wmWindowManager; @@ -46,14 +47,16 @@ typedef enum FileCheckType { CHECK_ALL = 3, } FileCheckType; -struct ListBase *folderlist_new(void); void folderlist_free(struct ListBase *folderlist); -struct ListBase *folderlist_duplicate(ListBase *folderlist); void folderlist_popdir(struct ListBase *folderlist, char *dir); void folderlist_pushdir(struct ListBase *folderlist, const char *dir); const char *folderlist_peeklastdir(struct ListBase *folderlist); int folderlist_clear_next(struct SpaceFile *sfile); +void folder_history_list_ensure_for_active_browse_mode(struct SpaceFile *sfile); +void folder_history_list_free(struct SpaceFile *sfile); +struct ListBase folder_history_list_duplicate(struct ListBase *listbase); + void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort); void filelist_sort(struct FileList *filelist); @@ -63,17 +66,22 @@ void filelist_setfilter_options(struct FileList *filelist, const bool hide_parent, const uint64_t filter, const uint64_t filter_id, + const bool filter_assets_only, const char *filter_glob, const char *filter_search); void filelist_filter(struct FileList *filelist); +void filelist_setlibrary(struct FileList *filelist, + const struct FileSelectAssetLibraryUID *asset_library); void filelist_init_icons(void); void filelist_free_icons(void); struct ImBuf *filelist_getimage(struct FileList *filelist, const int index); +struct ImBuf *filelist_file_getimage(const FileDirEntry *file); struct ImBuf *filelist_geticon_image(struct FileList *filelist, const int index); int filelist_geticon(struct FileList *filelist, const int index, const bool is_main); struct FileList *filelist_new(short type); +void filelist_settype(struct FileList *filelist, short type); void filelist_clear(struct FileList *filelist); void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection); void filelist_free(struct FileList *filelist); @@ -83,15 +91,18 @@ bool filelist_is_dir(struct FileList *filelist, const char *path); void filelist_setdir(struct FileList *filelist, char *r_dir); int filelist_files_ensure(struct FileList *filelist); -int filelist_empty(struct FileList *filelist); +int filelist_needs_reading(struct FileList *filelist); FileDirEntry *filelist_file(struct FileList *filelist, int index); int filelist_file_findpath(struct FileList *filelist, const char *file); +struct ID *filelist_file_get_id(const struct FileDirEntry *file); FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4]); void filelist_file_cache_slidingwindow_set(struct FileList *filelist, size_t window_size); bool filelist_file_cache_block(struct FileList *filelist, const int index); -bool filelist_force_reset(struct FileList *filelist); +bool filelist_needs_force_reset(struct FileList *filelist); +void filelist_tag_force_reset(struct FileList *filelist); bool filelist_pending(struct FileList *filelist); +bool filelist_needs_reset_on_main_changes(const struct FileList *filelist); bool filelist_is_ready(struct FileList *filelist); unsigned int filelist_entry_select_set(const struct FileList *filelist, diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 6e933e53a8f..b919a30e6cd 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -56,7 +56,9 @@ #include "BKE_appdir.h" #include "BKE_context.h" +#include "BKE_idtype.h" #include "BKE_main.h" +#include "BKE_preferences.h" #include "BLF_api.h" @@ -77,14 +79,67 @@ #define VERTLIST_MAJORCOLUMN_WIDTH (25 * UI_UNIT_X) -FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile) +static void fileselect_initialize_params_common(SpaceFile *sfile, FileSelectParams *params) { - if (!sfile) { - /* Sometimes called in poll before space type was checked. */ - return NULL; + const char *blendfile_path = BKE_main_blendfile_path_from_global(); + + /* operator has no setting for this */ + params->active_file = -1; + + if (!params->dir[0]) { + if (blendfile_path[0] != '\0') { + BLI_split_dir_part(blendfile_path, params->dir, sizeof(params->dir)); + } + else { + const char *doc_path = BKE_appdir_folder_default(); + if (doc_path) { + BLI_strncpy(params->dir, doc_path, sizeof(params->dir)); + } + } } - return sfile->params; + folder_history_list_ensure_for_active_browse_mode(sfile); + folderlist_pushdir(sfile->folders_prev, params->dir); + + /* Switching thumbnails needs to recalc layout T28809. */ + if (sfile->layout) { + sfile->layout->dirty = true; + } +} + +static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) +{ + BLI_assert(sfile->browse_mode == FILE_BROWSE_MODE_ASSETS); + BLI_assert(sfile->op == NULL); + + FileAssetSelectParams *asset_params = sfile->asset_params; + + if (!asset_params) { + asset_params = sfile->asset_params = MEM_callocN(sizeof(*asset_params), + "FileAssetSelectParams"); + asset_params->base_params.details_flags = U_default.file_space_data.details_flags; + asset_params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL; + asset_params->asset_library.custom_library_index = -1; + } + + FileSelectParams *base_params = &asset_params->base_params; + base_params->file[0] = '\0'; + base_params->filter_glob[0] = '\0'; + /* TODO this way of using filters to form categories is notably slower than specifying a + * "group" to read. That's because all types are read and filtering is applied afterwards. Would + * be nice if we could lazy-read individual groups. */ + base_params->flag |= U_default.file_space_data.flag | FILE_ASSETS_ONLY | FILE_FILTER; + base_params->flag &= ~FILE_DIRSEL_ONLY; + base_params->filter |= FILE_TYPE_BLENDERLIB; + base_params->filter_id = FILTER_ID_OB | FILTER_ID_GR; + base_params->display = FILE_IMGDISPLAY; + base_params->sort = FILE_SORT_ALPHA; + base_params->recursion_level = 1; + /* 'SMALL' size by default. More reasonable since this is typically used as regular editor, + * space is more of an issue here. */ + base_params->thumbnail_size = 96; + + fileselect_initialize_params_common(sfile, base_params); } /** @@ -92,6 +147,8 @@ FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile) * the previously used settings to be used here rather than overriding them */ static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile) { + BLI_assert(sfile->browse_mode == FILE_BROWSE_MODE_FILES); + FileSelectParams *params; wmOperator *op = sfile->op; @@ -297,42 +354,104 @@ static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile) params->filter_glob[0] = '\0'; } - /* operator has no setting for this */ - params->active_file = -1; + fileselect_initialize_params_common(sfile, params); - /* initialize the list with previous folders */ - if (!sfile->folders_prev) { - sfile->folders_prev = folderlist_new(); - } + return params; +} - if (!params->dir[0]) { - if (blendfile_path[0] != '\0') { - BLI_split_dir_part(blendfile_path, params->dir, sizeof(params->dir)); - } - else { - const char *doc_path = BKE_appdir_folder_default(); - if (doc_path) { - BLI_strncpy(params->dir, doc_path, sizeof(params->dir)); +/** + * If needed, create and return the file select parameters for the active browse mode. + */ +FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile) +{ + switch ((eFileBrowse_Mode)sfile->browse_mode) { + case FILE_BROWSE_MODE_FILES: + if (!sfile->params) { + fileselect_ensure_updated_file_params(sfile); } - } + return sfile->params; + case FILE_BROWSE_MODE_ASSETS: + if (!sfile->asset_params) { + fileselect_ensure_updated_asset_params(sfile); + } + return &sfile->asset_params->base_params; } - folderlist_pushdir(sfile->folders_prev, params->dir); + BLI_assert(!"Invalid browse mode set in file space."); + return NULL; +} - /* Switching thumbnails needs to recalc layout T28809. */ - if (sfile->layout) { - sfile->layout->dirty = true; +/** + * Get the file select parameters for the active browse mode. + */ +FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile) +{ + if (!sfile) { + /* Sometimes called in poll before space type was checked. */ + return NULL; } - return params; + switch ((eFileBrowse_Mode)sfile->browse_mode) { + case FILE_BROWSE_MODE_FILES: + return sfile->params; + case FILE_BROWSE_MODE_ASSETS: + return (FileSelectParams *)sfile->asset_params; + } + + BLI_assert(!"Invalid browse mode set in file space."); + return NULL; } -FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile) +FileSelectParams *ED_fileselect_get_file_params(const SpaceFile *sfile) { - if (!sfile->params) { - fileselect_ensure_updated_file_params(sfile); + return (sfile->browse_mode == FILE_BROWSE_MODE_FILES) ? sfile->params : NULL; +} + +FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile) +{ + return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) ? sfile->asset_params : NULL; +} + +static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) +{ + FileSelectAssetLibraryUID *library = &asset_params->asset_library; + FileSelectParams *base_params = &asset_params->base_params; + bUserAssetLibrary *user_library = NULL; + + /* Ensure valid repo, or fall-back to local one. */ + if (library->type == FILE_ASSET_LIBRARY_CUSTOM) { + BLI_assert(library->custom_library_index >= 0); + + user_library = BKE_preferences_asset_library_find_from_index(&U, + library->custom_library_index); + if (!user_library) { + library->type = FILE_ASSET_LIBRARY_LOCAL; + } + } + + switch (library->type) { + case FILE_ASSET_LIBRARY_LOCAL: + base_params->dir[0] = '\0'; + break; + case FILE_ASSET_LIBRARY_CUSTOM: + BLI_assert(user_library); + BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir)); + break; } - return sfile->params; + base_params->type = (library->type == FILE_ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB; +} + +void fileselect_refresh_params(SpaceFile *sfile) +{ + FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); + if (asset_params) { + fileselect_refresh_asset_params(asset_params); + } +} + +bool ED_fileselect_is_asset_browser(const SpaceFile *sfile) +{ + return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS); } /* The subset of FileSelectParams.flag items we store into preferences. Note that FILE_SORT_ALPHA @@ -371,6 +490,8 @@ void ED_fileselect_set_params_from_userdef(SpaceFile *sfile) wmOperator *op = sfile->op; UserDef_FileSpaceData *sfile_udata = &U.file_space_data; + sfile->browse_mode = FILE_BROWSE_MODE_FILES; + FileSelectParams *params = fileselect_ensure_updated_file_params(sfile); if (!op) { return; @@ -438,15 +559,6 @@ void ED_fileselect_params_to_userdef(SpaceFile *sfile, } } -void ED_fileselect_reset_params(SpaceFile *sfile) -{ - FileSelectParams *params = ED_fileselect_get_active_params(sfile); - params->type = FILE_UNIX; - params->flag = 0; - params->title[0] = '\0'; - params->active_file = -1; -} - /** * Sets FileSelectParams->file (name of selected file) */ @@ -1046,8 +1158,7 @@ void ED_fileselect_exit(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfil sfile->op = NULL; } - folderlist_free(sfile->folders_prev); - folderlist_free(sfile->folders_next); + folder_history_list_free(sfile); if (sfile->files) { ED_fileselect_clear(wm, owner_scene, sfile); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index c72ca58abba..774dc54700c 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -35,6 +35,8 @@ #include "BKE_screen.h" #include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_message.h" @@ -55,6 +57,23 @@ #include "filelist.h" #include "fsmenu.h" +static ARegion *file_ui_region_ensure(ScrArea *area, ARegion *region_prev) +{ + ARegion *region; + + if ((region = BKE_area_find_region_type(area, RGN_TYPE_UI)) != NULL) { + return region; + } + + region = MEM_callocN(sizeof(ARegion), "execute region for file"); + BLI_insertlinkafter(&area->regionbase, region_prev, region); + region->regiontype = RGN_TYPE_UI; + region->alignment = RGN_ALIGN_TOP; + region->flag = RGN_FLAG_DYNAMIC_SIZE; + + return region; +} + static ARegion *file_execute_region_ensure(ScrArea *area, ARegion *region_prev) { ARegion *region; @@ -149,22 +168,10 @@ static void file_free(SpaceLink *sl) sfile->files = NULL; } - if (sfile->folders_prev) { - folderlist_free(sfile->folders_prev); - MEM_freeN(sfile->folders_prev); - sfile->folders_prev = NULL; - } + folder_history_list_free(sfile); - if (sfile->folders_next) { - folderlist_free(sfile->folders_next); - MEM_freeN(sfile->folders_next); - sfile->folders_next = NULL; - } - - if (sfile->params) { - MEM_freeN(sfile->params); - sfile->params = NULL; - } + MEM_SAFE_FREE(sfile->params); + MEM_SAFE_FREE(sfile->asset_params); if (sfile->layout) { MEM_freeN(sfile->layout); @@ -205,19 +212,20 @@ static SpaceLink *file_duplicate(SpaceLink *sl) sfilen->previews_timer = NULL; sfilen->smoothscroll_timer = NULL; + FileSelectParams *active_params_old = ED_fileselect_get_active_params(sfileo); + if (active_params_old) { + sfilen->files = filelist_new(active_params_old->type); + filelist_setdir(sfilen->files, active_params_old->dir); + } + if (sfileo->params) { - sfilen->files = filelist_new(sfileo->params->type); sfilen->params = MEM_dupallocN(sfileo->params); - filelist_setdir(sfilen->files, sfilen->params->dir); } - - if (sfileo->folders_prev) { - sfilen->folders_prev = folderlist_duplicate(sfileo->folders_prev); + if (sfileo->asset_params) { + sfilen->asset_params = MEM_dupallocN(sfileo->asset_params); } - if (sfileo->folders_next) { - sfilen->folders_next = folderlist_duplicate(sfileo->folders_next); - } + sfilen->folder_histories = folder_history_list_duplicate(&sfileo->folder_histories); if (sfileo->layout) { sfilen->layout = MEM_dupallocN(sfileo->layout); @@ -232,15 +240,30 @@ static void file_ensure_valid_region_state(bContext *C, SpaceFile *sfile, FileSelectParams *params) { - ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI); - ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS); - ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE); + ARegion *region_tools = BKE_area_find_region_type(area, RGN_TYPE_TOOLS); bool needs_init = false; /* To avoid multiple ED_area_init() calls. */ + BLI_assert(region_tools); + + if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) { + ARegion *region_execute = file_execute_region_ensure(area, region_tools); + ARegion *region_props = file_tool_props_region_ensure(area, region_execute); + + /* Hide specific regions by default. */ + region_props->flag |= RGN_FLAG_HIDDEN; + region_execute->flag |= RGN_FLAG_HIDDEN; + + ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI); + if (region_ui) { + ED_region_remove(C, area, region_ui); + needs_init = true; + } + } /* If there's an file-operation, ensure we have the option and execute region */ - if (sfile->op && (region_props == NULL)) { - region_execute = file_execute_region_ensure(area, region_ui); - region_props = file_tool_props_region_ensure(area, region_execute); + else if (sfile->op) { + ARegion *region_ui = file_ui_region_ensure(area, region_tools); + ARegion *region_execute = file_execute_region_ensure(area, region_ui); + ARegion *region_props = file_tool_props_region_ensure(area, region_execute); if (params->flag & FILE_HIDE_TOOL_PROPS) { region_props->flag |= RGN_FLAG_HIDDEN; @@ -252,12 +275,19 @@ static void file_ensure_valid_region_state(bContext *C, needs_init = true; } /* If there's _no_ file-operation, ensure we _don't_ have the option and execute region */ - else if ((sfile->op == NULL) && (region_props != NULL)) { - BLI_assert(region_execute != NULL); + else { + ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS); + ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE); + ARegion *region_ui = file_ui_region_ensure(area, region_tools); + UNUSED_VARS(region_ui); - ED_region_remove(C, area, region_props); - ED_region_remove(C, area, region_execute); - needs_init = true; + if (region_props) { + BLI_assert(region_execute); + + ED_region_remove(C, area, region_props); + ED_region_remove(C, area, region_execute); + needs_init = true; + } } if (needs_init) { @@ -265,24 +295,42 @@ static void file_ensure_valid_region_state(bContext *C, } } +/** + * Tag the space to recreate the file-list. + */ +static void file_tag_reset_list(ScrArea *area, SpaceFile *sfile) +{ + filelist_tag_force_reset(sfile->files); + ED_area_tag_refresh(area); +} + static void file_refresh(const bContext *C, ScrArea *area) { wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_ensure_active_params(sfile); + FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); struct FSMenu *fsmenu = ED_fsmenu_get(); - if (!sfile->folders_prev) { - sfile->folders_prev = folderlist_new(); + fileselect_refresh_params(sfile); + folder_history_list_ensure_for_active_browse_mode(sfile); + + if (sfile->files && (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES) && + filelist_needs_reset_on_main_changes(sfile->files)) { + filelist_tag_force_reset(sfile->files); } + sfile->tags &= ~FILE_TAG_REBUILD_MAIN_FILES; + if (!sfile->files) { sfile->files = filelist_new(params->type); params->highlight_file = -1; /* added this so it opens nicer (ton) */ } + filelist_settype(sfile->files, params->type); filelist_setdir(sfile->files, params->dir); filelist_setrecursion(sfile->files, params->recursion_level); filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT); + filelist_setlibrary(sfile->files, asset_params ? &asset_params->asset_library : NULL); filelist_setfilter_options( sfile->files, (params->flag & FILE_FILTER) != 0, @@ -290,6 +338,7 @@ static void file_refresh(const bContext *C, ScrArea *area) true, /* Just always hide parent, prefer to not add an extra user option for this. */ params->filter, params->filter_id, + (params->flag & FILE_ASSETS_ONLY) != 0, params->filter_glob, params->filter_search); @@ -300,12 +349,12 @@ static void file_refresh(const bContext *C, ScrArea *area) sfile->bookmarknr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir); sfile->recentnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_RECENT, params->dir); - if (filelist_force_reset(sfile->files)) { + if (filelist_needs_force_reset(sfile->files)) { filelist_readjob_stop(wm, CTX_data_scene(C)); filelist_clear(sfile->files); } - if (filelist_empty(sfile->files)) { + if (filelist_needs_reading(sfile->files)) { if (!filelist_pending(sfile->files)) { filelist_readjob_start(sfile->files, C); } @@ -363,8 +412,21 @@ static void file_listener(wmWindow *UNUSED(win), ED_area_tag_refresh(area); } break; + case ND_SPACE_ASSET_PARAMS: + if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) { + ED_area_tag_refresh(area); + } + break; } break; + case NC_ASSET: { + if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) { + /* Full refresh of the file list if local asset data was changed. Refreshing this view is + * cheap and users expect this to be updated immediately. */ + file_tag_reset_list(area, sfile); + } + break; + } } } @@ -442,6 +504,22 @@ static void file_main_region_message_subscribe(const struct bContext *UNUSED(C), } } +static bool file_main_region_needs_refresh_before_draw(SpaceFile *sfile) +{ + /* Needed, because filelist is not initialized on loading */ + if (!sfile->files || filelist_needs_reading(sfile->files)) { + return true; + } + + /* File reading tagged the space because main data changed that may require a filelist reset. */ + if (filelist_needs_reset_on_main_changes(sfile->files) && + (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES)) { + return true; + } + + return false; +} + static void file_main_region_draw(const bContext *C, ARegion *region) { /* draw entirely, view changes should be handled here */ @@ -450,8 +528,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region) View2D *v2d = ®ion->v2d; - /* Needed, because filelist is not initialized on loading */ - if (!sfile->files || filelist_empty(sfile->files)) { + if (file_main_region_needs_refresh_before_draw(sfile)) { file_refresh(C, NULL); } @@ -497,7 +574,9 @@ static void file_main_region_draw(const bContext *C, ARegion *region) file_highlight_set(sfile, region, event->x, event->y); } - file_draw_list(C, region); + if (!file_draw_hint_if_invalid(sfile, region)) { + file_draw_list(C, region); + } /* reset view matrix */ UI_view2d_view_restore(C); @@ -681,6 +760,87 @@ static void file_dropboxes(void) WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy); } +static int file_space_subtype_get(ScrArea *area) +{ + SpaceFile *sfile = area->spacedata.first; + return sfile->browse_mode; +} + +static void file_space_subtype_set(ScrArea *area, int value) +{ + SpaceFile *sfile = area->spacedata.first; + sfile->browse_mode = value; +} + +static void file_space_subtype_item_extend(bContext *UNUSED(C), + EnumPropertyItem **item, + int *totitem) +{ + RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items); +} + +static const char *file_context_dir[] = {"active_file", "active_id", NULL}; + +static int /*eContextResult*/ file_context(const bContext *C, + const char *member, + bContextDataResult *result) +{ + bScreen *screen = CTX_wm_screen(C); + SpaceFile *sfile = CTX_wm_space_file(C); + FileSelectParams *params = ED_fileselect_get_active_params(sfile); + + BLI_assert(!ED_area_is_global(CTX_wm_area(C))); + + if (CTX_data_dir(member)) { + CTX_data_dir_set(result, file_context_dir); + return CTX_RESULT_OK; + } + + /* The following member checks return file-list data, check if that needs refreshing first. */ + if (file_main_region_needs_refresh_before_draw(sfile)) { + return CTX_RESULT_NO_DATA; + } + + if (CTX_data_equals(member, "active_file")) { + FileDirEntry *file = filelist_file(sfile->files, params->active_file); + if (file == NULL) { + return CTX_RESULT_NO_DATA; + } + + CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file); + return CTX_RESULT_OK; + } + if (CTX_data_equals(member, "id")) { + const FileDirEntry *file = filelist_file(sfile->files, params->active_file); + if (file == NULL) { + return CTX_RESULT_NO_DATA; + } + + ID *id = filelist_file_get_id(file); + if (id == NULL) { + return CTX_RESULT_NO_DATA; + } + + CTX_data_id_pointer_set(result, id); + return CTX_RESULT_OK; + } + + return CTX_RESULT_MEMBER_NOT_FOUND; +} + +static void file_id_remap(ScrArea *area, SpaceLink *sl, ID *UNUSED(old_id), ID *UNUSED(new_id)) +{ + SpaceFile *sfile = (SpaceFile *)sl; + + /* If the file shows main data (IDs), tag it for reset. */ + if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) { + /* Full refresh of the file list if main data was changed, don't even attempt remap pointers. + * We could give file list types a id-remap callback, but it's probably not worth it. + * Refreshing local file lists is relatively cheap. */ + file_tag_reset_list(area, sfile); + } +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_file(void) { @@ -700,6 +860,11 @@ void ED_spacetype_file(void) st->operatortypes = file_operatortypes; st->keymap = file_keymap; st->dropboxes = file_dropboxes; + st->space_subtype_item_extend = file_space_subtype_item_extend; + st->space_subtype_get = file_space_subtype_get; + st->space_subtype_set = file_space_subtype_set; + st->context = file_context; + st->id_remap = file_id_remap; /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype file region"); diff --git a/source/blender/editors/space_graph/CMakeLists.txt b/source/blender/editors/space_graph/CMakeLists.txt index 414e5c87f5a..2a795dd954c 100644 --- a/source/blender/editors/space_graph/CMakeLists.txt +++ b/source/blender/editors/space_graph/CMakeLists.txt @@ -34,8 +34,8 @@ set(SRC graph_draw.c graph_edit.c graph_ops.c - graph_slider_ops.c graph_select.c + graph_slider_ops.c graph_utils.c graph_view.c space_graph.c diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index c3e4eceef6e..e56d71913d6 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -2354,12 +2354,12 @@ static const EnumPropertyItem prop_graphkeys_mirror_types[] = { {GRAPHKEYS_MIRROR_YAXIS, "YAXIS", 0, - "By Times Over Time=0", + "By Times Over Zero Time", "Flip times of selected keyframes, effectively reversing the order they appear in"}, {GRAPHKEYS_MIRROR_XAXIS, "XAXIS", 0, - "By Values Over Value=0", + "By Values Over Zero Value", "Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"}, {GRAPHKEYS_MIRROR_MARKER, "MARKER", diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 12035ab6b61..4ab4ef518fb 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -1288,8 +1288,8 @@ void GRAPH_OT_select_less(wmOperatorType *ot) /* defines for left-right select tool */ static const EnumPropertyItem prop_graphkeys_leftright_select_types[] = { {GRAPHKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""}, - {GRAPHKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""}, - {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""}, + {GRAPHKEYS_LRSEL_LEFT, "LEFT", 0, "Before Current Frame", ""}, + {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", 0, "After Current Frame", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c index 0b9ba3762a3..cde0dab3503 100644 --- a/source/blender/editors/space_graph/graph_view.c +++ b/source/blender/editors/space_graph/graph_view.c @@ -533,4 +533,4 @@ void GRAPH_OT_ghost_curves_clear(wmOperatorType *ot) /* Flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -}
\ No newline at end of file +} diff --git a/source/blender/editors/space_info/info_report.c b/source/blender/editors/space_info/info_report.c index 3ba088018c3..7dd8382c8ef 100644 --- a/source/blender/editors/space_info/info_report.c +++ b/source/blender/editors/space_info/info_report.c @@ -396,7 +396,7 @@ void INFO_OT_report_copy(wmOperatorType *ot) { /* identifiers */ ot->name = "Copy Reports to Clipboard"; - ot->description = "Copy selected reports to Clipboard"; + ot->description = "Copy selected reports to clipboard"; ot->idname = "INFO_OT_report_copy"; /* api callbacks */ diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index d8f31161c20..f4a3bb96aeb 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -21,6 +21,7 @@ set(INC ../../blenkernel ../../blenlib ../../blentranslation + ../../compositor ../../depsgraph ../../draw ../../gpu @@ -29,7 +30,6 @@ set(INC ../../makesrna ../../nodes ../../render - ../../compositor ../../windowmanager ../../../../intern/glew-mx ../../../../intern/guardedalloc diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 84e7a74fab3..45f3b6cf9c9 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3182,6 +3182,46 @@ static void node_geometry_buts_attribute_math(uiLayout *layout, uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE); } +static void node_geometry_buts_point_instance(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "instance_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); +} + +static void node_geometry_buts_attribute_fill(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE); + // uiItemR(layout, ptr, "domain", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_geometry_buts_attribute_mix(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE); + uiLayout *col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "input_type_factor", DEFAULT_FLAGS, IFACE_("Factor"), ICON_NONE); + uiItemR(col, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("A"), ICON_NONE); + uiItemR(col, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("B"), ICON_NONE); +} + +static void node_geometry_buts_attribute_point_distribute(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribute_method", DEFAULT_FLAGS, "", ICON_NONE); +} + +static void node_geometry_buts_attribute_color_ramp(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiTemplateColorRamp(layout, ptr, "color_ramp", 0); +} + static void node_geometry_set_butfunc(bNodeType *ntype) { switch (ntype->type) { @@ -3194,12 +3234,27 @@ static void node_geometry_set_butfunc(bNodeType *ntype) case GEO_NODE_TRIANGULATE: ntype->draw_buttons = node_geometry_buts_triangulate; break; - case GEO_NODE_RANDOM_ATTRIBUTE: + case GEO_NODE_ATTRIBUTE_RANDOMIZE: ntype->draw_buttons = node_geometry_buts_random_attribute; break; case GEO_NODE_ATTRIBUTE_MATH: ntype->draw_buttons = node_geometry_buts_attribute_math; break; + case GEO_NODE_POINT_INSTANCE: + ntype->draw_buttons = node_geometry_buts_point_instance; + break; + case GEO_NODE_ATTRIBUTE_FILL: + ntype->draw_buttons = node_geometry_buts_attribute_fill; + break; + case GEO_NODE_ATTRIBUTE_MIX: + ntype->draw_buttons = node_geometry_buts_attribute_mix; + break; + case GEO_NODE_POINT_DISTRIBUTE: + ntype->draw_buttons = node_geometry_buts_attribute_point_distribute; + break; + case GEO_NODE_ATTRIBUTE_COLOR_RAMP: + ntype->draw_buttons = node_geometry_buts_attribute_color_ramp; + break; } } @@ -3389,6 +3444,7 @@ static const float std_node_socket_colors[][4] = { {0.93, 0.62, 0.36, 1.0}, /* SOCK_OBJECT */ {0.89, 0.76, 0.43, 1.0}, /* SOCK_IMAGE */ {0.00, 0.84, 0.64, 1.0}, /* SOCK_GEOMETRY */ + {0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */ }; /* common color callbacks for standard types */ @@ -3512,6 +3568,10 @@ static void std_node_socket_draw( uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); break; } + case SOCK_COLLECTION: { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + break; + } default: node_socket_button_label(C, layout, ptr, node_ptr, text); break; diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index fc7fa3a6caa..5a2eb0cc3a0 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -344,8 +344,6 @@ void node_from_view(struct bNode *node, float x, float y, float *rx, float *ry) /* based on settings in node, sets drawing rect info. each redraw! */ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) { - uiLayout *layout, *row; - PointerRNA nodeptr; RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); @@ -374,15 +372,15 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) PointerRNA sockptr; RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr); - layout = UI_block_layout(node->block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - locx + NODE_DYS, - dy, - NODE_WIDTH(node) - NODE_DY, - NODE_DY, - 0, - UI_style_get_dpi()); + uiLayout *layout = UI_block_layout(node->block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + locx + NODE_DYS, + dy, + NODE_WIDTH(node) - NODE_DY, + NODE_DY, + 0, + UI_style_get_dpi()); if (node->flag & NODE_MUTED) { uiLayoutSetActive(layout, false); @@ -393,7 +391,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) uiLayoutSetContextPointer(layout, "socket", &sockptr); /* align output buttons to the right */ - row = uiLayoutRow(layout, 1); + uiLayout *row = uiLayoutRow(layout, true); uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT); const char *socket_label = nodeSocketLabel(nsock); nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label)); @@ -469,15 +467,15 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) node->butr.ymin = 0; node->butr.ymax = 0; - layout = UI_block_layout(node->block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - locx + NODE_DYS, - dy, - node->butr.xmax, - 0, - 0, - UI_style_get_dpi()); + uiLayout *layout = UI_block_layout(node->block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + locx + NODE_DYS, + dy, + node->butr.xmax, + 0, + 0, + UI_style_get_dpi()); if (node->flag & NODE_MUTED) { uiLayoutSetActive(layout, false); @@ -502,15 +500,15 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) PointerRNA sockptr; RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr); - layout = UI_block_layout(node->block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - locx + NODE_DYS, - dy, - NODE_WIDTH(node) - NODE_DY, - NODE_DY, - 0, - UI_style_get_dpi()); + uiLayout *layout = UI_block_layout(node->block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + locx + NODE_DYS, + dy, + NODE_WIDTH(node) - NODE_DY, + NODE_DY, + 0, + UI_style_get_dpi()); if (node->flag & NODE_MUTED) { uiLayoutSetActive(layout, false); @@ -520,7 +518,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) uiLayoutSetContextPointer(layout, "node", &nodeptr); uiLayoutSetContextPointer(layout, "socket", &sockptr); - row = uiLayoutRow(layout, 1); + uiLayout *row = uiLayoutRow(layout, true); const char *socket_label = nodeSocketLabel(nsock); nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label)); @@ -771,7 +769,6 @@ void node_socket_color_get( bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, bNodeSocket *sock, float r_color[4]) { PointerRNA ptr; - BLI_assert(RNA_struct_is_a(node_ptr->type, &RNA_Node)); RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 039ddad71ef..fdce5e3f30b 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -100,9 +100,7 @@ typedef struct CompoJob { static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags) { - bNode *node; - - for (node = nodetree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &nodetree->nodes) { if (node->type == CMP_NODE_COMPOSITE) { if (recalc_flags & COM_RECALC_COMPOSITE) { node->flag |= NODE_DO_OUTPUT_RECALC; @@ -124,14 +122,12 @@ static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags) static int compo_get_recalc_flags(const bContext *C) { wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win; int recalc_flags = 0; - for (win = wm->windows.first; win; win = win->next) { + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { const bScreen *screen = WM_window_get_active_screen(win); - ScrArea *area; - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { if (area->spacetype == SPACE_IMAGE) { SpaceImage *sima = area->spacedata.first; if (sima->image) { @@ -247,7 +243,6 @@ static void compo_startjob(void *cjv, CompoJob *cj = cjv; bNodeTree *ntree = cj->localtree; Scene *scene = cj->scene; - SceneRenderView *srv; if (scene->use_nodes == false) { return; @@ -280,7 +275,7 @@ static void compo_startjob(void *cjv, ""); } else { - for (srv = scene->r.views.first; srv; srv = srv->next) { + LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) { if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) { continue; } @@ -309,8 +304,6 @@ static void compo_startjob(void *cjv, */ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner) { - wmJob *wm_job; - CompoJob *cj; Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -327,13 +320,13 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene BKE_image_backup_render( scene, BKE_image_ensure_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"), false); - wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - scene_owner, - "Compositing", - WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS, - WM_JOB_TYPE_COMPOSITE); - cj = MEM_callocN(sizeof(CompoJob), "compo job"); + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + scene_owner, + "Compositing", + WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS, + WM_JOB_TYPE_COMPOSITE); + CompoJob *cj = MEM_callocN(sizeof(CompoJob), "compo job"); /* customdata for preview thread */ cj->bmain = bmain; @@ -524,9 +517,6 @@ void ED_node_shader_default(const bContext *C, ID *id) /* called from shading buttons or header */ void ED_node_composit_default(const bContext *C, struct Scene *sce) { - bNode *in, *out; - bNodeSocket *fromsock, *tosock; - /* but lets check it anyway */ if (sce->nodetree) { if (G.debug & G_DEBUG) { @@ -541,18 +531,18 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce) sce->nodetree->edit_quality = NTREE_QUALITY_HIGH; sce->nodetree->render_quality = NTREE_QUALITY_HIGH; - out = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_COMPOSITE); + bNode *out = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_COMPOSITE); out->locx = 300.0f; out->locy = 400.0f; - in = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_R_LAYERS); + bNode *in = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_R_LAYERS); in->locx = 10.0f; in->locy = 400.0f; nodeSetActive(sce->nodetree, in); /* links from color to color */ - fromsock = in->outputs.first; - tosock = out->inputs.first; + bNodeSocket *fromsock = in->outputs.first; + bNodeSocket *tosock = out->inputs.first; nodeAddLink(sce->nodetree, in, fromsock, out, tosock); ntreeUpdateTree(CTX_data_main(C), sce->nodetree); @@ -562,9 +552,6 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce) /* called from shading buttons or header */ void ED_node_texture_default(const bContext *C, Tex *tex) { - bNode *in, *out; - bNodeSocket *fromsock, *tosock; - /* but lets check it anyway */ if (tex->nodetree) { if (G.debug & G_DEBUG) { @@ -575,17 +562,17 @@ void ED_node_texture_default(const bContext *C, Tex *tex) tex->nodetree = ntreeAddTree(NULL, "Texture Nodetree", ntreeType_Texture->idname); - out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT); + bNode *out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT); out->locx = 300.0f; out->locy = 300.0f; - in = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_CHECKER); + bNode *in = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_CHECKER); in->locx = 10.0f; in->locy = 300.0f; nodeSetActive(tex->nodetree, in); - fromsock = in->outputs.first; - tosock = out->inputs.first; + bNodeSocket *fromsock = in->outputs.first; + bNodeSocket *tosock = out->inputs.first; nodeAddLink(tex->nodetree, in, fromsock, out, tosock); ntreeUpdateTree(CTX_data_main(C), tex->nodetree); @@ -634,15 +621,13 @@ void snode_set_context(const bContext *C) void snode_update(SpaceNode *snode, bNode *node) { - bNodeTreePath *path; - /* XXX this only updates nodes in the current node space tree path. * The function supposedly should update any potential group node linking to changed tree, * this really requires a working depsgraph ... */ /* update all edited group nodes */ - path = snode->treepath.last; + bNodeTreePath *path = snode->treepath.last; if (path) { bNodeTree *ngroup = path->nodetree; for (path = path->prev; path; path = path->prev) { @@ -671,10 +656,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti /* generic node group output: set node as active output */ if (node->type == NODE_GROUP_OUTPUT) { - bNode *tnode; - for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { - if (tnode->type == NODE_GROUP_OUTPUT) { - tnode->flag &= ~NODE_DO_OUTPUT; + LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { + if (node_iter->type == NODE_GROUP_OUTPUT) { + node_iter->flag &= ~NODE_DO_OUTPUT; } } @@ -696,11 +680,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti SH_NODE_OUTPUT_WORLD, SH_NODE_OUTPUT_LIGHT, SH_NODE_OUTPUT_LINESTYLE)) { - bNode *tnode; - - for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { - if (tnode->type == node->type) { - tnode->flag &= ~NODE_DO_OUTPUT; + LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { + if (node_iter->type == node->type) { + node_iter->flag &= ~NODE_DO_OUTPUT; } } @@ -715,16 +697,13 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti /* if active texture changed, free glsl materials */ if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) { - Material *ma; - World *wo; - - for (ma = bmain->materials.first; ma; ma = ma->id.next) { + LISTBASE_FOREACH (Material *, ma, &bmain->materials) { if (ma->nodetree && ma->use_nodes && ntreeHasTree(ma->nodetree, ntree)) { GPU_material_free(&ma->gpumaterial); } } - for (wo = bmain->worlds.first; wo; wo = wo->id.next) { + LISTBASE_FOREACH (World *, wo, &bmain->materials) { if (wo->nodetree && wo->use_nodes && ntreeHasTree(wo->nodetree, ntree)) { GPU_material_free(&wo->gpumaterial); } @@ -742,11 +721,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti else if (ntree->type == NTREE_COMPOSIT) { /* make active viewer, currently only 1 supported... */ if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { - bNode *tnode; - - for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { - if (ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { - tnode->flag &= ~NODE_DO_OUTPUT; + LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { + if (ELEM(node_iter->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { + node_iter->flag &= ~NODE_DO_OUTPUT; } } @@ -760,11 +737,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti } else if (node->type == CMP_NODE_COMPOSITE) { if (was_output == 0) { - bNode *tnode; - - for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { - if (tnode->type == CMP_NODE_COMPOSITE) { - tnode->flag &= ~NODE_DO_OUTPUT; + LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { + if (node_iter->type == CMP_NODE_COMPOSITE) { + node_iter->flag &= ~NODE_DO_OUTPUT; } } @@ -879,14 +854,12 @@ static void edit_node_properties_get( /* is rct in visible part of node? */ static bNode *visible_node(SpaceNode *snode, const rctf *rct) { - bNode *node; - - for (node = snode->edittree->nodes.last; node; node = node->prev) { + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if (BLI_rctf_isect(&node->totr, rct, NULL)) { - break; + return node; } } - return node; + return NULL; } /* ********************** size widget operator ******************** */ @@ -952,23 +925,19 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event) ARegion *region = CTX_wm_region(C); bNode *node = nodeGetActive(snode->edittree); NodeSizeWidget *nsw = op->customdata; - float mx, my, dx, dy; switch (event->type) { - case MOUSEMOVE: - + case MOUSEMOVE: { + float mx, my; UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &mx, &my); - dx = (mx - nsw->mxstart) / UI_DPI_FAC; - dy = (my - nsw->mystart) / UI_DPI_FAC; + float dx = (mx - nsw->mxstart) / UI_DPI_FAC; + float dy = (my - nsw->mystart) / UI_DPI_FAC; if (node) { - float *pwidth; - float oldwidth, widthmin, widthmax; - - pwidth = &node->width; - oldwidth = nsw->oldwidth; - widthmin = node->typeinfo->minwidth; - widthmax = node->typeinfo->maxwidth; + float *pwidth = &node->width; + float oldwidth = nsw->oldwidth; + float widthmin = node->typeinfo->minwidth; + float widthmax = node->typeinfo->maxwidth; { if (nsw->directions & NODE_RESIZE_RIGHT) { @@ -1026,23 +995,24 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_region_tag_redraw(region); break; - + } case LEFTMOUSE: case MIDDLEMOUSE: - case RIGHTMOUSE: + case RIGHTMOUSE: { if (event->val == KM_RELEASE) { node_resize_exit(C, op, false); ED_node_post_apply_transform(C, snode->edittree); return OPERATOR_FINISHED; } - else if (event->val == KM_PRESS) { + if (event->val == KM_PRESS) { node_resize_exit(C, op, true); ED_region_tag_redraw(region); return OPERATOR_CANCELLED; } break; + } } return OPERATOR_RUNNING_MODAL; @@ -1095,14 +1065,12 @@ void NODE_OT_resize(wmOperatorType *ot) bool node_has_hidden_sockets(bNode *node) { - bNodeSocket *sock; - - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { if (sock->flag & SOCK_HIDDEN) { return true; } } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (sock->flag & SOCK_HIDDEN) { return true; } @@ -1112,24 +1080,22 @@ bool node_has_hidden_sockets(bNode *node) void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set) { - bNodeSocket *sock; - if (set == 0) { - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { sock->flag &= ~SOCK_HIDDEN; } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { sock->flag &= ~SOCK_HIDDEN; } } else { /* hide unused sockets */ - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { if (sock->link == NULL) { sock->flag |= SOCK_HIDDEN; } } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (nodeCountSocketLinks(snode->edittree, sock) == 0) { sock->flag |= SOCK_HIDDEN; } @@ -1142,16 +1108,13 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set) int node_find_indicated_socket( SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, float cursor[2], int in_out) { - bNode *node; - bNodeSocket *sock; rctf rect; *nodep = NULL; *sockp = NULL; /* check if we click in a socket */ - for (node = snode->edittree->nodes.first; node; node = node->next) { - + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { BLI_rctf_init_pt_radius(&rect, cursor, NODE_SOCKSIZE + 4); if (!(node->flag & NODE_HIDDEN)) { @@ -1167,7 +1130,7 @@ int node_find_indicated_socket( } if (in_out & SOCK_IN) { - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { if (!nodeSocketIsHidden(sock)) { if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { if (node == visible_node(snode, &rect)) { @@ -1180,7 +1143,7 @@ int node_find_indicated_socket( } } if (in_out & SOCK_OUT) { - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (!nodeSocketIsHidden(sock)) { if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { if (node == visible_node(snode, &rect)) { @@ -1226,17 +1189,15 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - bNode *node, *newnode, *lastnode; - bNodeLink *link, *newlink, *lastlink; const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs"); bool do_tag_update = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - lastnode = ntree->nodes.last; - for (node = ntree->nodes.first; node; node = node->next) { + bNode *lastnode = ntree->nodes.last; + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & SELECT) { - newnode = BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT); + BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT); /* to ensure redraws or rerenders happen */ ED_node_tag_update_id(snode->id); @@ -1251,14 +1212,14 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) /* copy links between selected nodes * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy! */ - lastlink = ntree->links.last; - for (link = ntree->links.first; link; link = link->next) { + bNodeLink *lastlink = ntree->links.last; + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { /* This creates new links between copied nodes. * If keep_inputs is set, also copies input links from unselected (when fromnode==NULL)! */ if (link->tonode && (link->tonode->flag & NODE_SELECT) && (keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT)))) { - newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink"); + bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink"); newlink->flag = link->flag; newlink->tonode = link->tonode->new_node; newlink->tosock = link->tosock->new_sock; @@ -1282,11 +1243,11 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) } /* clear flags for recursive depth-first iteration */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node->flag &= ~NODE_TEST; } /* reparent copied nodes */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if ((node->flag & SELECT) && !(node->flag & NODE_TEST)) { node_duplicate_reparent_recursive(node); } @@ -1298,10 +1259,10 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) } /* deselect old nodes, select the copies instead */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & SELECT) { /* has been set during copy above */ - newnode = node->new_node; + bNode *newnode = node->new_node; nodeSetSelected(node, false); node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE); @@ -1389,17 +1350,16 @@ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); - Scene *curscene = CTX_data_scene(C), *scene; - bNode *node; + Scene *curscene = CTX_data_scene(C); ED_preview_kill_jobs(CTX_wm_manager(C), bmain); /* first tag scenes unread */ - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { scene->id.tag |= LIB_TAG_DOIT; } - for (node = snode->edittree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if (node->type == CMP_NODE_R_LAYERS) { ID *id = node->id; if (id->tag & LIB_TAG_DOIT) { @@ -1434,13 +1394,14 @@ void NODE_OT_read_viewlayers(wmOperatorType *ot) int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *sce = CTX_data_scene(C); - bNode *node; /* This is actually a test whether scene is used by the compositor or not. * All the nodes are using same render result, so there is no need to do * anything smart about check how exactly scene is used. */ - for (node = sce->nodetree->nodes.first; node; node = node->next) { - if (node->id == (ID *)sce) { + bNode *node = NULL; + LISTBASE_FOREACH (bNode *, node_iter, &sce->nodetree->nodes) { + if (node_iter->id == (ID *)sce) { + node = node_iter; break; } } @@ -1486,14 +1447,14 @@ void NODE_OT_render_changed(wmOperatorType *ot) static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag) { - bNode *node; int tot_eq = 0, tot_neq = 0; /* Toggles the flag on all selected nodes. * If the flag is set on all nodes it is unset. * If the flag is not set on all nodes, it is set. */ - for (node = snode->edittree->nodes.first; node; node = node->next) { + + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if (node->flag & SELECT) { if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) { @@ -1512,7 +1473,7 @@ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag) } } } - for (node = snode->edittree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if (node->flag & SELECT) { if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) { @@ -1631,8 +1592,6 @@ void NODE_OT_options_toggle(wmOperatorType *ot) static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceNode *snode = CTX_wm_space_node(C); - bNode *node; - int hidden; /* sanity checking (poll callback checks this already) */ if ((snode == NULL) || (snode->edittree == NULL)) { @@ -1642,17 +1601,17 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op)) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); /* Toggle for all selected nodes */ - hidden = 0; - for (node = snode->edittree->nodes.first; node; node = node->next) { + bool hidden = false; + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if (node->flag & SELECT) { if (node_has_hidden_sockets(node)) { - hidden = 1; + hidden = true; break; } } } - for (node = snode->edittree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if (node->flag & SELECT) { node_set_hidden_sockets(snode, node, !hidden); } @@ -1686,12 +1645,11 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); - bNode *node; bool do_tag_update = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - for (node = snode->edittree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { /* Only allow muting of nodes having a mute func! */ if ((node->flag & SELECT) && node->typeinfo->update_internal_links) { node->flag ^= NODE_MUTED; @@ -1731,13 +1689,11 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); - bNode *node, *next; bool do_tag_update = false; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - for (node = snode->edittree->nodes.first; node; node = next) { - next = node->next; + LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { if (node->flag & SELECT) { do_tag_update |= (do_tag_update || node_connected_to_output(bmain, snode->edittree, node)); nodeRemoveNode(bmain, snode->edittree, node, true); @@ -1787,10 +1743,8 @@ static bool node_switch_view_poll(bContext *C) static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceNode *snode = CTX_wm_space_node(C); - bNode *node, *next; - for (node = snode->edittree->nodes.first; node; node = next) { - next = node->next; + LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { if (node->flag & SELECT) { /* call the update function from the Switch View node */ node->update = NODE_UPDATE_OPERATOR; @@ -1825,12 +1779,10 @@ static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); - bNode *node, *next; ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - for (node = snode->edittree->nodes.first; node; node = next) { - next = node->next; + LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) { if (node->flag & SELECT) { nodeInternalRelink(snode->edittree, node); nodeRemoveNode(bmain, snode->edittree, node, true); @@ -1963,9 +1915,6 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) SpaceNode *snode = CTX_wm_space_node(C); PointerRNA ptr = CTX_data_pointer_get(C, "node"); bNode *node = NULL; - NodeImageMultiFile *nimf; - bNodeSocket *sock; - int direction; if (ptr.data) { node = ptr.data; @@ -1978,14 +1927,14 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - nimf = node->storage; + NodeImageMultiFile *nimf = node->storage; - sock = BLI_findlink(&node->inputs, nimf->active_input); + bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input); if (!sock) { return OPERATOR_CANCELLED; } - direction = RNA_enum_get(op->ptr, "direction"); + int direction = RNA_enum_get(op->ptr, "direction"); if (direction == 1) { bNodeSocket *before = sock->prev; @@ -2037,24 +1986,23 @@ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - bNode *node, *tnode; if (!ntree) { return OPERATOR_CANCELLED; } - node = nodeGetActive(ntree); + bNode *node = nodeGetActive(ntree); if (!node) { return OPERATOR_CANCELLED; } - for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { - if (tnode->flag & NODE_SELECT && tnode != node) { + LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) { + if (node_iter->flag & NODE_SELECT && node_iter != node) { if (node->flag & NODE_CUSTOM_COLOR) { - tnode->flag |= NODE_CUSTOM_COLOR; - copy_v3_v3(tnode->color, node->color); + node_iter->flag |= NODE_CUSTOM_COLOR; + copy_v3_v3(node_iter->color, node->color); } else { - tnode->flag &= ~NODE_CUSTOM_COLOR; + node_iter->flag &= ~NODE_CUSTOM_COLOR; } } } @@ -2086,8 +2034,6 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - bNode *node; - bNodeLink *link, *newlink; ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); @@ -2095,7 +2041,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) BKE_node_clipboard_clear(); BKE_node_clipboard_init(ntree); - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & SELECT) { /* No ID refcounting, this node is virtual, * detached from any actual Blender data currently. */ @@ -2105,7 +2051,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) } } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & SELECT) { bNode *new_node = node->new_node; @@ -2126,11 +2072,11 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) /* copy links between selected nodes * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy! */ - for (link = ntree->links.first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { /* This creates new links between copied nodes. */ if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode && (link->fromnode->flag & NODE_SELECT)) { - newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink"); + bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink"); newlink->flag = link->flag; newlink->tonode = link->tonode->new_node; newlink->tosock = link->tosock->new_sock; @@ -2165,18 +2111,11 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) { SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - const ListBase *clipboard_nodes_lb; - const ListBase *clipboard_links_lb; - bNode *node; - bNodeLink *link; - int num_nodes; - float center[2]; - bool is_clipboard_valid, all_nodes_valid; /* validate pointers in the clipboard */ - is_clipboard_valid = BKE_node_clipboard_validate(); - clipboard_nodes_lb = BKE_node_clipboard_get_nodes(); - clipboard_links_lb = BKE_node_clipboard_get_links(); + bool is_clipboard_valid = BKE_node_clipboard_validate(); + const ListBase *clipboard_nodes_lb = BKE_node_clipboard_get_nodes(); + const ListBase *clipboard_links_lb = BKE_node_clipboard_get_links(); if (BLI_listbase_is_empty(clipboard_nodes_lb)) { BKE_report(op->reports, RPT_ERROR, "Clipboard is empty"); @@ -2196,8 +2135,8 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) } /* make sure all clipboard nodes would be valid in the target tree */ - all_nodes_valid = true; - for (node = clipboard_nodes_lb->first; node; node = node->next) { + bool all_nodes_valid = true; + LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { if (!node->typeinfo->poll_instance || !node->typeinfo->poll_instance(node, ntree)) { all_nodes_valid = false; BKE_reportf(op->reports, @@ -2217,15 +2156,16 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) node_deselect_all(snode); /* calculate "barycenter" for placing on mouse cursor */ - zero_v2(center); - for (node = clipboard_nodes_lb->first, num_nodes = 0; node; node = node->next, num_nodes++) { + float center[2] = {0.0f, 0.0f}; + int num_nodes = 0; + LISTBASE_FOREACH_INDEX (bNode *, node, clipboard_nodes_lb, num_nodes) { center[0] += BLI_rctf_cent_x(&node->totr); center[1] += BLI_rctf_cent_y(&node->totr); } mul_v2_fl(center, 1.0 / num_nodes); /* copy nodes from clipboard */ - for (node = clipboard_nodes_lb->first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { bNode *new_node = BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT); /* pasted nodes are selected */ @@ -2233,14 +2173,14 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) } /* reparent copied nodes */ - for (node = clipboard_nodes_lb->first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { bNode *new_node = node->new_node; if (new_node->parent) { new_node->parent = new_node->parent->new_node; } } - for (link = clipboard_links_lb->first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, clipboard_links_lb) { nodeAddLink(ntree, link->fromnode->new_node, link->fromsock->new_sock, @@ -2275,10 +2215,9 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot) static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb) { - bNodeSocket *sock; - for (sock = lb->first; sock; sock = sock->next) { - if (sock->flag & SELECT) { - return sock; + LISTBASE_FOREACH (bNodeSocket *, socket, lb) { + if (socket->flag & SELECT) { + return socket; } } return NULL; @@ -2289,12 +2228,12 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op) SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; int in_out = RNA_enum_get(op->ptr, "in_out"); - PointerRNA ntree_ptr; - bNodeSocket *sock, *tsock, *active_sock; - const char *default_name; + PointerRNA ntree_ptr; RNA_id_pointer_create((ID *)ntree, &ntree_ptr); + const char *default_name; + bNodeSocket *active_sock; if (in_out == SOCK_IN) { active_sock = ntree_get_active_interface_socket(&ntree->inputs); default_name = "Input"; @@ -2304,6 +2243,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op) default_name = "Output"; } + bNodeSocket *sock; if (active_sock) { /* insert a copy of the active socket right after it */ sock = ntreeInsertSocketInterface( @@ -2317,11 +2257,11 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op) } /* deactivate sockets (has to check both lists) */ - for (tsock = ntree->inputs.first; tsock; tsock = tsock->next) { - tsock->flag &= ~SELECT; + LISTBASE_FOREACH (bNodeSocket *, socket_iter, &ntree->inputs) { + socket_iter->flag &= ~SELECT; } - for (tsock = ntree->outputs.first; tsock; tsock = tsock->next) { - tsock->flag &= ~SELECT; + LISTBASE_FOREACH (bNodeSocket *, socket_iter, &ntree->outputs) { + socket_iter->flag &= ~SELECT; } /* make the new socket active */ sock->flag |= SELECT; @@ -2359,9 +2299,8 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - bNodeSocket *iosock, *active_sock; - iosock = ntree_get_active_interface_socket(&ntree->inputs); + bNodeSocket *iosock = ntree_get_active_interface_socket(&ntree->inputs); if (!iosock) { iosock = ntree_get_active_interface_socket(&ntree->outputs); } @@ -2370,7 +2309,7 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *UNUSED(op)) } /* preferably next socket becomes active, otherwise try previous socket */ - active_sock = (iosock->next ? iosock->next : iosock->prev); + bNodeSocket *active_sock = (iosock->next ? iosock->next : iosock->prev); ntreeRemoveSocketInterface(ntree, iosock); /* set active socket */ @@ -2416,11 +2355,9 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op) SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; int direction = RNA_enum_get(op->ptr, "direction"); - bNodeSocket *iosock; - ListBase *lb; - lb = &ntree->inputs; - iosock = ntree_get_active_interface_socket(lb); + ListBase *lb = &ntree->inputs; + bNodeSocket *iosock = ntree_get_active_interface_socket(lb); if (!iosock) { lb = &ntree->outputs; iosock = ntree_get_active_interface_socket(lb); @@ -2489,8 +2426,6 @@ static bool node_shader_script_update_poll(bContext *C) Scene *scene = CTX_data_scene(C); const RenderEngineType *type = RE_engines_find(scene->r.engine); SpaceNode *snode = CTX_wm_space_node(C); - bNode *node; - Text *text; /* test if we have a render engine that supports shaders scripts */ if (!(type && type->update_script_node)) { @@ -2498,7 +2433,7 @@ static bool node_shader_script_update_poll(bContext *C) } /* see if we have a shader script node in context */ - node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data; + bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data; if (!node && snode && snode->edittree) { node = nodeGetActive(snode->edittree); @@ -2513,7 +2448,7 @@ static bool node_shader_script_update_poll(bContext *C) } /* see if we have a text datablock in context */ - text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; + Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data; if (text) { return 1; } @@ -2530,12 +2465,11 @@ static bool node_shader_script_update_text_recursive(RenderEngine *engine, Text *text) { bool found = false; - bNode *node; ntree->done = true; /* update each script that is using this text datablock */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == NODE_GROUP) { bNodeTree *ngroup = (bNodeTree *)node->id; if (ngroup && !ngroup->done) { @@ -2557,18 +2491,16 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); SpaceNode *snode = CTX_wm_space_node(C); PointerRNA nodeptr = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript); - bNodeTree *ntree_base = NULL; - bNode *node = NULL; - RenderEngine *engine; - RenderEngineType *type; bool found = false; /* setup render engine */ - type = RE_engines_find(scene->r.engine); - engine = RE_engine_create(type); + RenderEngineType *type = RE_engines_find(scene->r.engine); + RenderEngine *engine = RE_engine_create(type); engine->reports = op->reports; /* get node */ + bNodeTree *ntree_base = NULL; + bNode *node = NULL; if (nodeptr.data) { ntree_base = (bNodeTree *)nodeptr.owner_id; node = nodeptr.data; @@ -2643,10 +2575,8 @@ static void viewer_border_corner_to_backdrop(SpaceNode *snode, float *fx, float *fy) { - float bufx, bufy; - - bufx = backdrop_width * snode->zoom; - bufy = backdrop_height * snode->zoom; + float bufx = backdrop_width * snode->zoom; + float bufy = backdrop_height * snode->zoom; *fx = (bufx > 0.0f ? ((float)x - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f); *fy = (bufy > 0.0f ? ((float)y - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f); @@ -2655,14 +2585,12 @@ static void viewer_border_corner_to_backdrop(SpaceNode *snode, static int viewer_border_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Image *ima; void *lock; - ImBuf *ibuf; ED_preview_kill_jobs(CTX_wm_manager(C), bmain); - ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); if (ibuf) { ARegion *region = CTX_wm_region(C); diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c index 3c861896d37..d2c88ed787c 100644 --- a/source/blender/editors/space_node/node_view.c +++ b/source/blender/editors/space_node/node_view.c @@ -283,7 +283,7 @@ void NODE_OT_backimage_move(wmOperatorType *ot) { /* identifiers */ ot->name = "Background Image Move"; - ot->description = "Move Node backdrop"; + ot->description = "Move node backdrop"; ot->idname = "NODE_OT_backimage_move"; /* api callbacks */ diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index afc1a963f4f..ad7632377a3 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -645,7 +645,7 @@ static bool node_ima_drop_poll(bContext *UNUSED(C), /* rule might not work? */ return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE)); } - return WM_drag_ID(drag, ID_IM) != NULL; + return WM_drag_get_local_ID(drag, ID_IM) != NULL; } static bool node_mask_drop_poll(bContext *UNUSED(C), @@ -653,19 +653,19 @@ static bool node_mask_drop_poll(bContext *UNUSED(C), const wmEvent *UNUSED(event), const char **UNUSED(r_tooltip)) { - return WM_drag_ID(drag, ID_MSK) != NULL; + return WM_drag_get_local_ID(drag, ID_MSK) != NULL; } static void node_id_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_ID(drag, 0); + ID *id = WM_drag_get_local_ID(drag, 0); RNA_string_set(drop->ptr, "name", id->name + 2); } static void node_id_path_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_ID(drag, 0); + ID *id = WM_drag_get_local_ID(drag, 0); if (id) { RNA_string_set(drop->ptr, "name", id->name + 2); diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 74f99540bee..e0262371559 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -34,6 +34,7 @@ set(INC set(SRC outliner_collections.c + outliner_context.c outliner_dragdrop.c outliner_draw.c outliner_edit.c @@ -46,12 +47,12 @@ set(SRC space_outliner.c tree/common.cc tree/tree_display.cc + tree/tree_display_data.cc tree/tree_display_libraries.cc - tree/tree_display_view_layer.cc - tree/tree_display_sequencer.cc tree/tree_display_orphaned.cc tree/tree_display_scenes.cc - tree/tree_display_data.cc + tree/tree_display_sequencer.cc + tree/tree_display_view_layer.cc tree/tree_element.cc tree/tree_element_anim_data.cc tree/tree_element_driver_base.cc diff --git a/source/blender/editors/space_outliner/outliner_context.c b/source/blender/editors/space_outliner/outliner_context.c new file mode 100644 index 00000000000..a314a640e42 --- /dev/null +++ b/source/blender/editors/space_outliner/outliner_context.c @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spoutliner + */ + +#include "BLI_listbase.h" + +#include "BKE_context.h" + +#include "DNA_space_types.h" + +#include "RNA_access.h" + +#include "outliner_intern.h" + +static void outliner_context_selected_ids_recursive(const ListBase *subtree, + bContextDataResult *result) +{ + LISTBASE_FOREACH (const TreeElement *, te, subtree) { + const TreeStoreElem *tse = TREESTORE(te); + if ((tse->flag & TSE_SELECTED) && (ELEM(tse->type, 0, TSE_LAYER_COLLECTION))) { + CTX_data_id_list_add(result, tse->id); + } + outliner_context_selected_ids_recursive(&te->subtree, result); + } +} + +static void outliner_context_selected_ids(const SpaceOutliner *space_outliner, + bContextDataResult *result) +{ + outliner_context_selected_ids_recursive(&space_outliner->tree, result); + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); +} + +static const char *outliner_context_dir[] = {"selected_ids", NULL}; + +int /*eContextResult*/ outliner_context(const bContext *C, + const char *member, + bContextDataResult *result) +{ + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + + if (CTX_data_dir(member)) { + CTX_data_dir_set(result, outliner_context_dir); + return CTX_RESULT_OK; + } + if (CTX_data_equals(member, "selected_ids")) { + outliner_context_selected_ids(space_outliner, result); + return CTX_RESULT_OK; + } + /* Note: Querying non-ID selection could also work if tree elements stored their matching RNA + * struct type. */ + + return CTX_RESULT_MEMBER_NOT_FOUND; +} diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index d3da7b80765..152bbc96281 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -333,7 +333,7 @@ static bool parent_drop_poll(bContext *C, ED_region_tag_redraw_no_rebuild(CTX_wm_region(C)); } - Object *potential_child = (Object *)WM_drag_ID(drag, ID_OB); + Object *potential_child = (Object *)WM_drag_get_local_ID(drag, ID_OB); if (!potential_child) { return false; } @@ -421,7 +421,7 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) } Object *par = (Object *)tselem->id; - Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB); + Object *ob = (Object *)WM_drag_get_local_ID_from_event(event, ID_OB); if (ELEM(NULL, ob, par)) { return OPERATOR_CANCELLED; @@ -445,7 +445,7 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) void OUTLINER_OT_parent_drop(wmOperatorType *ot) { /* identifiers */ - ot->name = "Drop to Set Parent [+Alt keeps transforms]"; + ot->name = "Drop to Set Parent (hold Alt to keep transforms)"; ot->description = "Drag to parent in Outliner"; ot->idname = "OUTLINER_OT_parent_drop"; @@ -473,7 +473,7 @@ static bool parent_clear_poll(bContext *C, } } - Object *ob = (Object *)WM_drag_ID(drag, ID_OB); + Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB); if (!ob) { return false; } @@ -531,7 +531,7 @@ static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven void OUTLINER_OT_parent_clear(wmOperatorType *ot) { /* identifiers */ - ot->name = "Drop to Clear Parent [+Alt keeps transforms]"; + ot->name = "Drop to Clear Parent (hold Alt to keep transforms)"; ot->description = "Drag to clear parent in Outliner"; ot->idname = "OUTLINER_OT_parent_clear"; @@ -552,7 +552,7 @@ static bool scene_drop_poll(bContext *C, const char **UNUSED(r_tooltip)) { /* Ensure item under cursor is valid drop target */ - Object *ob = (Object *)WM_drag_ID(drag, ID_OB); + Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB); return (ob && (outliner_ID_drop_find(C, event, ID_SCE) != NULL)); } @@ -560,7 +560,7 @@ static int scene_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent { Main *bmain = CTX_data_main(C); Scene *scene = (Scene *)outliner_ID_drop_find(C, event, ID_SCE); - Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB); + Object *ob = (Object *)WM_drag_get_local_ID_from_event(event, ID_OB); if (ELEM(NULL, ob, scene) || ID_IS_LINKED(scene)) { return OPERATOR_CANCELLED; @@ -620,7 +620,7 @@ static bool material_drop_poll(bContext *C, const char **UNUSED(r_tooltip)) { /* Ensure item under cursor is valid drop target */ - Material *ma = (Material *)WM_drag_ID(drag, ID_MA); + Material *ma = (Material *)WM_drag_get_local_ID(drag, ID_MA); return (ma && (outliner_ID_drop_find(C, event, ID_OB) != NULL)); } @@ -628,7 +628,7 @@ static int material_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve { Main *bmain = CTX_data_main(C); Object *ob = (Object *)outliner_ID_drop_find(C, event, ID_OB); - Material *ma = (Material *)WM_drag_ID_from_event(event, ID_MA); + Material *ma = (Material *)WM_drag_get_local_ID_from_event(event, ID_MA); if (ELEM(NULL, ob, ma)) { return OPERATOR_CANCELLED; @@ -1461,14 +1461,14 @@ static int outliner_item_drag_drop_invoke(bContext *C, parent = scene->master_collection; } - WM_drag_add_ID(drag, id, &parent->id); + WM_drag_add_local_ID(drag, id, &parent->id); } BLI_freelistN(&selected.selected_array); } else { /* Add single ID. */ - WM_drag_add_ID(drag, data.drag_id, data.drag_parent); + WM_drag_add_local_ID(drag, data.drag_id, data.drag_parent); } ED_outliner_select_sync_from_outliner(C, space_outliner); diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 0294b4836c8..339cc3068d0 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -42,6 +42,7 @@ struct TreeElement; struct TreeStoreElem; struct ViewLayer; struct bContext; +struct bContextDataResult; struct bPoseChannel; struct wmKeyConfig; struct wmOperatorType; @@ -561,6 +562,12 @@ void outliner_tag_redraw_avoid_rebuild_on_open_change(const struct SpaceOutliner void outliner_sync_selection(const struct bContext *C, struct SpaceOutliner *space_outliner); +/* outliner_context.c ------------------------------------------- */ + +int outliner_context(const struct bContext *C, + const char *member, + struct bContextDataResult *result); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 159a4616ba7..492fc5c23bc 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -737,7 +737,7 @@ static void id_local_fn(bContext *C, } static void object_proxy_to_override_convert_fn(bContext *C, - ReportList *UNUSED(reports), + ReportList *reports, Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), @@ -754,8 +754,15 @@ static void object_proxy_to_override_convert_fn(bContext *C, return; } - BKE_lib_override_library_proxy_convert( - CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy); + if (!BKE_lib_override_library_proxy_convert( + CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy)) { + BKE_reportf( + reports, + RPT_ERROR_INVALID_INPUT, + "Could not create a library override from proxy '%s' (might use already local data?)", + ob_proxy->id.name + 2); + return; + } DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE); WM_event_add_notifier(C, NC_WINDOW, NULL); @@ -1685,6 +1692,8 @@ typedef enum eOutlinerIdOpTypes { OUTLINER_IDOP_INVALID = 0, OUTLINER_IDOP_UNLINK, + OUTLINER_IDOP_MARK_ASSET, + OUTLINER_IDOP_CLEAR_ASSET, OUTLINER_IDOP_LOCAL, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, @@ -1710,6 +1719,8 @@ typedef enum eOutlinerIdOpTypes { /* TODO: implement support for changing the ID-block used. */ static const EnumPropertyItem prop_id_op_types[] = { {OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""}, + {OUTLINER_IDOP_MARK_ASSET, "MARK_ASSET", 0, "Mark Asset", ""}, + {OUTLINER_IDOP_CLEAR_ASSET, "CLEAR_ASSET", 0, "Clear Asset", ""}, {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""}, {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""}, {OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""}, @@ -1915,6 +1926,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } break; } + case OUTLINER_IDOP_MARK_ASSET: { + WM_operator_name_call(C, "ASSET_OT_mark", WM_OP_EXEC_DEFAULT, NULL); + break; + } + case OUTLINER_IDOP_CLEAR_ASSET: { + WM_operator_name_call(C, "ASSET_OT_clear", WM_OP_EXEC_DEFAULT, NULL); + break; + } case OUTLINER_IDOP_LOCAL: { /* make local */ outliner_do_libdata_operation( @@ -2791,7 +2810,7 @@ static int do_outliner_operation_event(bContext *C, } if (datalevel == TSE_ID_BASE) { /* do nothing... there are no ops needed here yet */ - return 0; + return OPERATOR_CANCELLED; } if (datalevel == TSE_CONSTRAINT) { return outliner_operator_menu(C, "OUTLINER_OT_constraint_operation"); @@ -2802,7 +2821,7 @@ static int do_outliner_operation_event(bContext *C, return outliner_operator_menu(C, "OUTLINER_OT_data_operation"); } - return 0; + return OPERATOR_CANCELLED; } static int outliner_operation(bContext *C, wmOperator *op, const wmEvent *event) diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 7308b161d18..56eedcd3748 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -908,9 +908,9 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, /** * TODO: this function needs to be split up! It's getting a bit too large... * - * \note: "ID" is not always a real ID - * \note: If child items are only added to the tree if the item is open, the TSE_ type _must_ be - * added to #outliner_element_needs_rebuild_on_open_change(). + * \note "ID" is not always a real ID. + * \note If child items are only added to the tree if the item is open, + * the `TSE_` type _must_ be added to #outliner_element_needs_rebuild_on_open_change(). */ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, ListBase *lb, diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 3d675fdd9e4..c7c207caca0 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -451,6 +451,7 @@ void ED_spacetype_outliner(void) st->dropboxes = outliner_dropboxes; st->id_remap = outliner_id_remap; st->deactivate = outliner_deactivate; + st->context = outliner_context; /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype outliner region"); diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc index bd0870c837c..cb5f42f08e1 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc @@ -24,6 +24,8 @@ #include "BKE_collection.h" #include "BKE_main.h" +#include "DNA_collection_types.h" + #include "BLT_translation.h" #include "../outliner_intern.h" diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc index c88eb957dd1..f7740f4648f 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc @@ -20,6 +20,7 @@ #include <iostream> +#include "DNA_collection_types.h" #include "DNA_scene_types.h" #include "BKE_layer.h" diff --git a/source/blender/editors/space_outliner/tree/tree_element_nla.hh b/source/blender/editors/space_outliner/tree/tree_element_nla.hh index 3ca62b13bd8..c94287ce576 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_nla.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_nla.hh @@ -23,7 +23,6 @@ #include "tree_element.hh" struct NlaTrack; -struct NlaStrip; namespace blender::ed::outliner { diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c index bde7bdb77f1..50cfa2e71c7 100644 --- a/source/blender/editors/space_script/script_edit.c +++ b/source/blender/editors/space_script/script_edit.c @@ -152,7 +152,7 @@ void SCRIPT_OT_reload(wmOperatorType *ot) { /* identifiers */ ot->name = "Reload Scripts"; - ot->description = "Reload Scripts"; + ot->description = "Reload scripts"; ot->idname = "SCRIPT_OT_reload"; /* api callbacks */ diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 37dfcdbc765..71433a6978a 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -81,9 +81,18 @@ typedef struct SequencerAddData { #define SEQPROP_ENDFRAME (1 << 1) #define SEQPROP_NOPATHS (1 << 2) #define SEQPROP_NOCHAN (1 << 3) +#define SEQPROP_FIT_METHOD (1 << 4) #define SELECT 1 +static const EnumPropertyItem scale_fit_methods[] = { + {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"}, + {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"}, + {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image to fill the canvas"}, + {SEQ_USE_ORIGINAL_SIZE, "ORIGINAL", 0, "Use Original Size", "Keep image at its original size"}, + {0, NULL, 0, NULL, NULL}, +}; + static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) { PropertyRNA *prop; @@ -123,6 +132,15 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) prop = RNA_def_boolean( ot->srna, "overlap", 0, "Allow Overlap", "Don't correct overlap on new sequence strips"); RNA_def_property_flag(prop, PROP_HIDDEN); + + if (flag & SEQPROP_FIT_METHOD) { + ot->prop = RNA_def_enum(ot->srna, + "fit_method", + scale_fit_methods, + SEQ_SCALE_TO_FIT, + "Fit Method", + "Scale fit method"); + } } static void sequencer_generic_invoke_path__internal(bContext *C, @@ -206,6 +224,8 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperato seq_load->end_frame = seq_load->start_frame; seq_load->channel = RNA_int_get(op->ptr, "channel"); seq_load->len = 1; + seq_load->fit_method = RNA_enum_get(op->ptr, "fit_method"); + SEQ_tool_settings_fit_method_set(CTX_data_scene(C), seq_load->fit_method); if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) { /* Full path, file is set by the caller. */ @@ -659,6 +679,7 @@ static int sequencer_add_movie_strip_invoke(bContext *C, if (ed && ed->seqbasep && ed->seqbasep->first) { RNA_boolean_set(op->ptr, "use_framerate", false); } + RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene)); /* This is for drag and drop. */ if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) || @@ -725,7 +746,7 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot) WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); - sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME); + sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_FIT_METHOD); RNA_def_boolean(ot->srna, "sound", true, "Sound", "Load sound with the movie"); RNA_def_boolean(ot->srna, "use_framerate", @@ -928,6 +949,9 @@ static int sequencer_add_image_strip_invoke(bContext *C, PropertyRNA *prop; Scene *scene = CTX_data_scene(C); + const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + RNA_enum_set(op->ptr, "fit_method", tool_settings->fit_method); + /* Name set already by drag and drop. */ if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) { sequencer_generic_invoke_xy__internal( @@ -972,7 +996,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot) WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); - sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME); + sequencer_generic_props__internal(ot, + SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD); RNA_def_boolean(ot->srna, "use_placeholders", diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 081714991ff..d7d601a3c76 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -227,16 +227,16 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. * \param stepsize: The width of a pixel. */ -static void draw_seq_waveform(View2D *v2d, - const bContext *C, - SpaceSeq *sseq, - Scene *scene, - Sequence *seq, - float x1, - float y1, - float x2, - float y2, - float stepsize) +static void draw_seq_waveform_overlay(View2D *v2d, + const bContext *C, + SpaceSeq *sseq, + Scene *scene, + Sequence *seq, + float x1, + float y1, + float x2, + float y2, + float stepsize) { /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ int x1_offset = max_ff(v2d->cur.xmin, x1); @@ -603,121 +603,110 @@ static void draw_seq_outline(Sequence *seq, } } -/* Draw info text on a sequence strip. */ -static void draw_seq_text(View2D *v2d, - Sequence *seq, - SpaceSeq *sseq, - float x1, - float x2, - float y1, - float y2, - bool seq_active, - bool y_threshold) +static const char *draw_seq_text_get_name(Sequence *seq) { - rctf rect; - char str[32 + FILE_MAX]; - size_t str_len; const char *name = seq->name + 2; - uchar col[4]; - - /* All strings should include name. */ if (name[0] == '\0') { name = BKE_sequence_give_name(seq); } + return name; +} - if (ELEM(seq->type, SEQ_TYPE_META, SEQ_TYPE_ADJUSTMENT)) { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); +static void draw_seq_text_get_source(Sequence *seq, char *r_source, size_t source_len) +{ + /* Set source for the most common types. */ + if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE)) { + BLI_snprintf(r_source, source_len, "%s%s", seq->strip->dir, seq->strip->stripdata->name); } - else if (seq->type == SEQ_TYPE_SCENE) { - if (seq->scene) { - if (seq->scene_camera) { - str_len = BLI_snprintf(str, - sizeof(str), - "%s: %s (%s) | %d", - name, - seq->scene->id.name + 2, - ((ID *)seq->scene_camera)->name + 2, - seq->len); - } - else { - str_len = BLI_snprintf( - str, sizeof(str), "%s: %s | %d", name, seq->scene->id.name + 2, seq->len); - } - } - else { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); + else if (seq->type == SEQ_TYPE_SOUND_RAM) { + if (seq->sound) { + BLI_snprintf(r_source, source_len, "%s", seq->sound->filepath); } } - else if (seq->type == SEQ_TYPE_MOVIECLIP) { - if (seq->clip && !STREQ(name, seq->clip->id.name + 2)) { - str_len = BLI_snprintf( - str, sizeof(str), "%s: %s | %d", name, seq->clip->id.name + 2, seq->len); - } - else { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); - } + else if (seq->type == SEQ_TYPE_MULTICAM) { + BLI_snprintf(r_source, source_len, "Channel: %d", seq->multicam_source); } - else if (seq->type == SEQ_TYPE_MASK) { - if (seq->mask && !STREQ(name, seq->mask->id.name + 2)) { - str_len = BLI_snprintf( - str, sizeof(str), "%s: %s | %d", name, seq->mask->id.name + 2, seq->len); + else if (seq->type == SEQ_TYPE_TEXT) { + TextVars *textdata = seq->effectdata; + BLI_snprintf(r_source, source_len, "%s", textdata->text); + } + else if (seq->type == SEQ_TYPE_SCENE) { + if (seq->scene_camera) { + BLI_snprintf(r_source, + source_len, + "%s (%s)", + seq->scene->id.name + 2, + ((ID *)seq->scene_camera)->name + 2); } else { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); + BLI_snprintf(r_source, source_len, "%s", seq->scene->id.name + 2); } } - else if (seq->type == SEQ_TYPE_MULTICAM) { - str_len = BLI_snprintf(str, sizeof(str), "Cam %s: %d", name, seq->multicam_source); - } - else if (seq->type == SEQ_TYPE_IMAGE) { - str_len = BLI_snprintf(str, - sizeof(str), - "%s: %s%s | %d", - name, - seq->strip->dir, - seq->strip->stripdata->name, - seq->len); + else if (seq->type == SEQ_TYPE_MOVIECLIP) { + BLI_snprintf(r_source, source_len, "%s", seq->clip->id.name + 2); } - else if (seq->type == SEQ_TYPE_TEXT) { - TextVars *textdata = seq->effectdata; - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", textdata->text, seq->startdisp); + else if (seq->type == SEQ_TYPE_MASK) { + BLI_snprintf(r_source, source_len, "%s", seq->mask->id.name + 2); } - else if (seq->type & SEQ_TYPE_EFFECT) { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); + else { + *r_source = '\0'; } - else if (seq->type == SEQ_TYPE_SOUND_RAM) { - /* If a waveform is drawn, avoid to draw text when there is not enough vertical space. */ - if (!y_threshold && (sseq->flag & SEQ_NO_WAVEFORMS) == 0 && - ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { +} - str[0] = 0; - str_len = 0; - } - else if (seq->sound) { - str_len = BLI_snprintf( - str, sizeof(str), "%s: %s | %d", name, seq->sound->filepath, seq->len); +static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq, + Sequence *seq, + char *r_overlay_string, + size_t overlay_string_len) +{ + const char *name = draw_seq_text_get_name(seq); + char source[FILE_MAX]; + int strip_duration = seq->enddisp - seq->startdisp; + draw_seq_text_get_source(seq, source, sizeof(source)); + + bool show_name = sseq->flag & SEQ_SHOW_STRIP_NAME; + bool show_source = (sseq->flag & (SEQ_SHOW_STRIP_SOURCE)) && source[0] != '\0'; + bool show_duration = sseq->flag & SEQ_SHOW_STRIP_DURATION; + + size_t string_len = 0; + if (show_name) { + string_len = BLI_snprintf(r_overlay_string, overlay_string_len, "%s", name); + if (show_source || show_duration) { + string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, " | "); } - else { - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); + } + if (show_source) { + string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, "%s", source); + if (show_duration) { + string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, " | "); } } - else if (seq->type == SEQ_TYPE_MOVIE) { - str_len = BLI_snprintf(str, - sizeof(str), - "%s: %s%s | %d", - name, - seq->strip->dir, - seq->strip->stripdata->name, - seq->len); + if (show_duration) { + string_len += BLI_snprintf( + r_overlay_string + string_len, overlay_string_len, "%d", strip_duration); } - else { - /* Should never get here!, but might with files from future. */ - BLI_assert(0); + return string_len; +} + +/* Draw info text on a sequence strip. */ +static void draw_seq_text_overlay(View2D *v2d, + Sequence *seq, + SpaceSeq *sseq, + float x1, + float x2, + float y1, + float y2, + bool seq_active) +{ + char overlay_string[FILE_MAX]; + size_t overlay_string_len = draw_seq_text_get_overlay_string( + sseq, seq, overlay_string, sizeof(overlay_string)); - str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len); + if (overlay_string_len == 0) { + return; } /* White text for the active strip. */ + uchar col[4]; col[0] = col[1] = col[2] = seq_active ? 255 : 10; col[3] = 255; @@ -731,15 +720,16 @@ static void draw_seq_text(View2D *v2d, } } + rctf rect; rect.xmin = x1; rect.ymin = y1; rect.xmax = x2; rect.ymax = y2; - UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col); + UI_view2d_text_cache_add_rectf(v2d, &rect, overlay_string, overlay_string_len, col); } -static void draw_sequence_extensions(Scene *scene, Sequence *seq, uint pos, float pixely) +static void draw_sequence_extensions_overlay(Scene *scene, Sequence *seq, uint pos, float pixely) { float x1, x2, y1, y2; uchar col[4], blend_col[3]; @@ -988,7 +978,7 @@ static void fcurve_batch_add_verts(GPUVertBuf *vbo, * - Volume for sound strips. * - Opacity for the other types. */ -static void draw_seq_fcurve( +static void draw_seq_fcurve_overlay( Scene *scene, View2D *v2d, Sequence *seq, float x1, float y1, float x2, float y2, float pixelx) { FCurve *fcu; @@ -1085,11 +1075,21 @@ static void draw_seq_strip(const bContext *C, x2 = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; y2 = seq->machine + SEQ_STRIP_OFSTOP; - /* Calculate height needed for drawing text on strip. */ - float text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely); + float text_margin_y; + bool y_threshold; + if ((sseq->flag & SEQ_SHOW_STRIP_NAME) || (sseq->flag & SEQ_SHOW_STRIP_SOURCE) || + (sseq->flag & SEQ_SHOW_STRIP_DURATION)) { - /* Is there enough space for drawing something else than text? */ - bool y_threshold = ((y2 - y1) / pixely) > 20 * U.dpi_fac; + /* Calculate height needed for drawing text on strip. */ + text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely); + + /* Is there enough space for drawing something else than text? */ + y_threshold = ((y2 - y1) / pixely) > 20 * U.dpi_fac; + } + else { + text_margin_y = y2; + y_threshold = 1; + } uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1102,12 +1102,13 @@ static void draw_seq_strip(const bContext *C, } /* Draw strip offsets when flag is enabled or during "solo preview". */ - if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) { - if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) { - draw_sequence_extensions(scene, seq, pos, pixely); + if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) { + if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) { + draw_sequence_extensions_overlay(scene, seq, pos, pixely); + } } } - immUnbindProgram(); x1 = seq->startdisp; @@ -1118,24 +1119,24 @@ static void draw_seq_strip(const bContext *C, drawmeta_contents(scene, seq, x1, y1, x2, y2); } - if (sseq->flag & SEQ_SHOW_FCURVES) { - draw_seq_fcurve(scene, v2d, seq, x1, y1, x2, y2, pixelx); + if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY) && (sseq->flag & SEQ_SHOW_FCURVES)) { + draw_seq_fcurve_overlay(scene, v2d, seq, x1, y1, x2, y2, pixelx); } /* Draw sound strip waveform. */ - if ((seq->type == SEQ_TYPE_SOUND_RAM) && (sseq->flag & SEQ_NO_WAVEFORMS) == 0) { - draw_seq_waveform(v2d, - C, - sseq, - scene, - seq, - x1, - y_threshold ? y1 + 0.05f : y1, - x2, - y_threshold ? text_margin_y : y2, - BLI_rctf_size_x(®ion->v2d.cur) / region->winx); + if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) && + (sseq->flag & SEQ_NO_WAVEFORMS) == 0) { + draw_seq_waveform_overlay(v2d, + C, + sseq, + scene, + seq, + x1, + y_threshold ? y1 + 0.05f : y1, + x2, + y_threshold ? text_margin_y : y2, + BLI_rctf_size_x(®ion->v2d.cur) / region->winx); } - /* Draw locked state. */ if (seq->flag & SEQ_LOCK) { draw_seq_locked(x1, y1, x2, y2); @@ -1162,11 +1163,21 @@ static void draw_seq_strip(const bContext *C, calculate_seq_text_offsets(v2d, seq, &x1, &x2, pixelx); - /* Don't draw strip if there is not enough vertical or horizontal space. */ - if (((x2 - x1) > 32 * pixelx * U.dpi_fac) && ((y2 - y1) > 8 * pixely * U.dpi_fac)) { - /* Depending on the vertical space, draw text on top or in the center of strip. */ - draw_seq_text( - v2d, seq, sseq, x1, x2, y_threshold ? text_margin_y : y1, y2, seq_active, y_threshold); + /* If a waveform is drawn, avoid drawing text when there is not enough vertical space. */ + if (seq->type == SEQ_TYPE_SOUND_RAM) { + if (!y_threshold && (sseq->flag & SEQ_NO_WAVEFORMS) == 0 && + ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { + return; + } + } + + if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { + /* Don't draw strip if there is not enough vertical or horizontal space. */ + if (((x2 - x1) > 32 * pixelx * U.dpi_fac) && ((y2 - y1) > 8 * pixely * U.dpi_fac)) { + /* Depending on the vertical space, draw text on top or in the center of strip. */ + draw_seq_text_overlay( + v2d, seq, sseq, x1, x2, y_threshold ? text_margin_y : y1, y2, seq_active); + } } } @@ -1360,7 +1371,7 @@ static void sequencer_display_size(Scene *scene, float r_viewrect[2]) r_viewrect[0] *= scene->r.xasp / scene->r.yasp; } -static void sequencer_draw_gpencil(const bContext *C) +static void sequencer_draw_gpencil_overlay(const bContext *C) { /* Draw grease-pencil (image aligned). */ ED_annotation_draw_2dimage(C); @@ -1373,7 +1384,9 @@ static void sequencer_draw_gpencil(const bContext *C) } /* Draw content and safety borders borders. */ -static void sequencer_draw_borders(const SpaceSeq *sseq, const View2D *v2d, const Scene *scene) +static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, + const View2D *v2d, + const Scene *scene) { float x1 = v2d->tot.xmin; float y1 = v2d->tot.ymin; @@ -1825,17 +1838,17 @@ void sequencer_draw_preview(const bContext *C, C, scene, region, sseq, ibuf, scope, draw_overlay, draw_backdrop); /* Draw over image. */ - if (sseq->flag & SEQ_SHOW_METADATA) { + if (sseq->flag & SEQ_SHOW_METADATA && sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { ED_region_image_metadata_draw(0.0, 0.0, ibuf, &v2d->tot, 1.0, 1.0); } } - if (show_imbuf) { - sequencer_draw_borders(sseq, v2d, scene); + if (show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + sequencer_draw_borders_overlay(sseq, v2d, scene); } - if (draw_gpencil && show_imbuf) { - sequencer_draw_gpencil(C); + if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + sequencer_draw_gpencil_overlay(C); } #if 0 sequencer_draw_maskedit(C, scene, region, sseq); @@ -2292,7 +2305,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region) UI_view2d_view_ortho(v2d); /* Get timeline bound-box, needed for the scroll-bars. */ - boundbox_seq(scene, &v2d->tot); + SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &v2d->tot); draw_seq_backdrop(v2d); UI_view2d_constant_grid_draw(v2d, FPS); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 93b17830c0f..ddc9ba2e0f6 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -183,117 +183,13 @@ bool sequencer_view_strips_poll(bContext *C) /** \name Remove Gaps Operator * \{ */ -static bool sequence_offset_after_frame(Scene *scene, const int delta, const int timeline_frame) -{ - Sequence *seq; - Editing *ed = BKE_sequencer_editing_get(scene, false); - bool done = false; - TimeMarker *marker; - - /* All strips >= timeline_frame are shifted. */ - - if (ed == NULL) { - return 0; - } - - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (seq->startdisp >= timeline_frame) { - BKE_sequence_translate(scene, seq, delta); - BKE_sequence_calc(scene, seq); - BKE_sequence_invalidate_cache_preprocessed(scene, seq); - done = true; - } - } - - if (!scene->toolsettings->lock_markers) { - for (marker = scene->markers.first; marker; marker = marker->next) { - if (marker->frame >= timeline_frame) { - marker->frame += delta; - } - } - } - - return done; -} - -void boundbox_seq(Scene *scene, rctf *rect) -{ - Sequence *seq; - Editing *ed = BKE_sequencer_editing_get(scene, false); - float min[2], max[2]; - - if (ed == NULL) { - return; - } - - min[0] = SFRA; - max[0] = EFRA + 1; - min[1] = 0.0; - max[1] = 8.0; - - seq = ed->seqbasep->first; - while (seq) { - - if (min[0] > seq->startdisp - 1) { - min[0] = seq->startdisp - 1; - } - if (max[0] < seq->enddisp + 1) { - max[0] = seq->enddisp + 1; - } - if (max[1] < seq->machine + 2) { - max[1] = seq->machine + 2; - } - - seq = seq->next; - } - - rect->xmin = min[0]; - rect->xmax = max[0]; - rect->ymin = min[1]; - rect->ymax = max[1]; -} - static int sequencer_gap_remove_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - rctf rectf; - int timeline_frame, efra, sfra; - bool first = false, done; - bool do_all = RNA_boolean_get(op->ptr, "all"); - - /* Get first and last frame. */ - boundbox_seq(scene, &rectf); - sfra = (int)rectf.xmin; - efra = (int)rectf.xmax; - - /* Check if the current frame has a gap already. */ - for (timeline_frame = CFRA; timeline_frame >= sfra; timeline_frame--) { - if (SEQ_render_evaluate_frame(scene, timeline_frame)) { - first = true; - break; - } - } + const bool do_all = RNA_boolean_get(op->ptr, "all"); + const Editing *ed = BKE_sequencer_editing_get(scene, false); - for (; timeline_frame < efra; timeline_frame++) { - /* There's still no strip to remove a gap for. */ - if (first == false) { - if (SEQ_render_evaluate_frame(scene, timeline_frame)) { - first = true; - } - } - else if (SEQ_render_evaluate_frame(scene, timeline_frame) == 0) { - done = true; - while (SEQ_render_evaluate_frame(scene, timeline_frame) == 0) { - done = sequence_offset_after_frame(scene, -1, timeline_frame); - if (done == false) { - break; - } - } - if (done == false || do_all == false) { - break; - } - } - } + SEQ_edit_remove_gaps(scene, ed->seqbasep, CFRA, do_all); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -330,9 +226,9 @@ void SEQUENCER_OT_gap_remove(struct wmOperatorType *ot) static int sequencer_gap_insert_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - int frames = RNA_int_get(op->ptr, "frames"); - - sequence_offset_after_frame(scene, frames, CFRA); + const int frames = RNA_int_get(op->ptr, "frames"); + const Editing *ed = BKE_sequencer_editing_get(scene, false); + SEQ_offset_after_frame(scene, ed->seqbasep, frames, CFRA); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -537,7 +433,7 @@ static int slip_add_sequences_recursive( for (seq = seqbasep->first; seq; seq = seq->next) { if (!do_trim || (!(seq->type & SEQ_TYPE_EFFECT) && (seq->flag & SELECT))) { seq_array[offset + num_items] = seq; - trim[offset + num_items] = do_trim; + trim[offset + num_items] = do_trim && ((seq->type & SEQ_TYPE_EFFECT) == 0); num_items++; if (seq->type == SEQ_TYPE_META) { @@ -545,9 +441,6 @@ static int slip_add_sequences_recursive( num_items += slip_add_sequences_recursive( &seq->seqbase, seq_array, trim, num_items + offset, false); } - else if (seq->type & SEQ_TYPE_EFFECT) { - trim[offset + num_items] = false; - } } } @@ -2634,7 +2527,7 @@ void ED_sequencer_deselect_all(Scene *scene) SEQ_CURRENT_END; } -static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op)) +static int sequencer_paste_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -2643,8 +2536,25 @@ static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op)) int ofs; Sequence *iseq, *iseq_first; + if (BLI_listbase_count(&seqbase_clipboard) == 0) { + BKE_report(op->reports, RPT_INFO, "No strips to paste"); + return OPERATOR_CANCELLED; + } + ED_sequencer_deselect_all(scene); - ofs = scene->r.cfra - seqbase_clipboard_frame; + if (RNA_boolean_get(op->ptr, "keep_offset")) { + ofs = scene->r.cfra - seqbase_clipboard_frame; + } + else { + int min_seq_startdisp = INT_MAX; + LISTBASE_FOREACH (Sequence *, seq, &seqbase_clipboard) { + if (seq->startdisp < min_seq_startdisp) { + min_seq_startdisp = seq->startdisp; + } + } + /* Paste strips after playhead. */ + ofs = scene->r.cfra - min_seq_startdisp; + } /* Copy strips, temporarily restoring pointers to actual data-blocks. This * must happen on the clipboard itself, so that copying does user counting @@ -2692,6 +2602,11 @@ void SEQUENCER_OT_paste(wmOperatorType *ot) /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + PropertyRNA *prop = RNA_def_boolean( + ot->srna, "keep_offset", false, "Keep Offset", "Keep strip offset to playhead when pasting"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -3499,3 +3414,148 @@ Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[ } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Clear Strip Transform Operator + * \{ */ + +enum { + STRIP_TRANSFORM_POSITION, + STRIP_TRANSFORM_SCALE, + STRIP_TRANSFORM_ROTATION, + STRIP_TRANSFORM_ALL, +}; + +static const EnumPropertyItem transform_reset_properties[] = { + {STRIP_TRANSFORM_POSITION, "POSITION", 0, "Position", "Reset strip transform location"}, + {STRIP_TRANSFORM_SCALE, "SCALE", 0, "Scale", "Reset strip transform scale"}, + {STRIP_TRANSFORM_ROTATION, "ROTATION", 0, "Rotation", "Reset strip transform rotation"}, + {STRIP_TRANSFORM_ALL, "ALL", 0, "All", "Reset strip transform location, scale and rotation"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int sequencer_strip_transform_clear_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + const Editing *ed = BKE_sequencer_editing_get(scene, false); + Sequence *seq; + const int property = RNA_enum_get(op->ptr, "property"); + + for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) { + StripTransform *transform = seq->strip->transform; + switch (property) { + case STRIP_TRANSFORM_POSITION: + transform->xofs = 0; + transform->yofs = 0; + break; + case STRIP_TRANSFORM_SCALE: + transform->scale_x = 1.0f; + transform->scale_y = 1.0f; + break; + case STRIP_TRANSFORM_ROTATION: + transform->rotation = 0.0f; + break; + case STRIP_TRANSFORM_ALL: + transform->xofs = 0; + transform->yofs = 0; + transform->scale_x = 1.0f; + transform->scale_y = 1.0f; + transform->rotation = 0.0f; + break; + } + BKE_sequence_invalidate_cache_preprocessed(scene, seq); + } + } + + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Clear Strip Transform"; + ot->idname = "SEQUENCER_OT_strip_transform_clear"; + ot->description = "Reset image transformation to default value"; + + /* Api callbacks. */ + ot->exec = sequencer_strip_transform_clear_exec; + ot->poll = sequencer_edit_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, + "property", + transform_reset_properties, + STRIP_TRANSFORM_ALL, + "Property", + "Strip transform property to be reset"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform Set Fit Operator + * \{ */ + +static const EnumPropertyItem scale_fit_methods[] = { + {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image so fits in preview"}, + {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image so it fills preview completely"}, + {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image so it fills preview"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int sequencer_strip_transform_fit_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + const Editing *ed = BKE_sequencer_editing_get(scene, false); + Sequence *seq; + const eSeqImageFitMethod fit_method = RNA_enum_get(op->ptr, "fit_method"); + + for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) { + const int timeline_frame = CFRA; + StripElem *strip_elem = SEQ_render_give_stripelem(seq, timeline_frame); + + if (strip_elem == NULL) { + continue; + } + + SEQ_set_scale_to_fit(seq, + strip_elem->orig_width, + strip_elem->orig_height, + scene->r.xsch, + scene->r.ysch, + fit_method); + BKE_sequence_invalidate_cache_preprocessed(scene, seq); + } + } + + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Strip Transform Set Fit"; + ot->idname = "SEQUENCER_OT_strip_transform_fit"; + + /* Api callbacks. */ + ot->exec = sequencer_strip_transform_fit_exec; + ot->poll = sequencer_edit_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, + "fit_method", + scale_fit_methods, + SEQ_SCALE_TO_FIT, + "Fit Method", + "Scale fit fit_method"); +} + +/** \} */ diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 1ea4fb05d53..4c942a83f2b 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -71,7 +71,6 @@ struct ImBuf *sequencer_ibuf_get(struct Main *bmain, /* sequencer_edit.c */ struct View2D; void seq_rectf(struct Sequence *seq, struct rctf *rectf); -void boundbox_seq(struct Scene *scene, struct rctf *rect); struct Sequence *find_nearest_seq(struct Scene *scene, struct View2D *v2d, int *hand, @@ -145,6 +144,8 @@ void SEQUENCER_OT_enable_proxies(struct wmOperatorType *ot); void SEQUENCER_OT_export_subtitles(struct wmOperatorType *ot); void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot); +void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot); +void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot); /* sequencer_select.c */ void SEQUENCER_OT_select_all(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index bdf6e4ece7f..7bfc8600544 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -81,6 +81,8 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_change_path); WM_operatortype_append(SEQUENCER_OT_set_range_to_strips); + WM_operatortype_append(SEQUENCER_OT_strip_transform_clear); + WM_operatortype_append(SEQUENCER_OT_strip_transform_fit); /* sequencer_select.c */ WM_operatortype_append(SEQUENCER_OT_select_all); diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 75d92d5f00d..e12c43b7804 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -87,8 +87,10 @@ static int sequencer_view_all_exec(bContext *C, wmOperator *op) rctf box; const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + Scene *scene = CTX_data_scene(C); + const Editing *ed = BKE_sequencer_editing_get(scene, false); - boundbox_seq(CTX_data_scene(C), &box); + SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &box); UI_view2d_smooth_view(C, region, &box, smooth_viewtx); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 45c7bac54f8..2bf4741e4f5 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -99,7 +99,8 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce sseq->view = SEQ_VIEW_SEQUENCE; sseq->mainb = SEQ_DRAW_IMG_IMBUF; sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | - SEQ_ZOOM_TO_FIT; + SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME | + SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_DURATION; /* Tool header. */ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); @@ -706,7 +707,8 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) SpaceSeq *sseq = area->spacedata.first; Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); - const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW)); + const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && + (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)); /* XXX temp fix for wrong setting in sseq->mainb */ if (sseq->mainb == SEQ_DRAW_SEQUENCE) { diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index bb71a9b11be..0f5ac5abe1d 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -357,7 +357,7 @@ static bool text_drop_paste_poll(bContext *UNUSED(C), static void text_drop_paste(wmDrag *drag, wmDropBox *drop) { char *text; - ID *id = WM_drag_ID(drag, 0); + ID *id = WM_drag_get_local_ID(drag, 0); /* copy drag path to properties */ text = RNA_path_full_ID_py(G_MAIN, id); diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index f8b7c62686f..932bacfb8a0 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -2588,7 +2588,7 @@ static int text_scroll_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - txt_screen_skip(st, region, lines * U.wheellinescroll); + txt_screen_skip(st, region, lines * 3); ED_area_tag_redraw(CTX_wm_area(C)); diff --git a/source/blender/editors/space_userpref/userpref_ops.c b/source/blender/editors/space_userpref/userpref_ops.c index d823530fd89..ee23cde78c2 100644 --- a/source/blender/editors/space_userpref/userpref_ops.c +++ b/source/blender/editors/space_userpref/userpref_ops.c @@ -30,6 +30,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_main.h" +#include "BKE_preferences.h" #include "BKE_report.h" #include "RNA_access.h" @@ -133,9 +134,71 @@ static void PREFERENCES_OT_autoexec_path_remove(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Add Asset Library Operator + * \{ */ + +static int preferences_asset_library_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + BKE_preferences_asset_library_add(&U, NULL, NULL); + U.runtime.is_dirty = true; + return OPERATOR_FINISHED; +} + +static void PREFERENCES_OT_asset_library_add(wmOperatorType *ot) +{ + ot->name = "Add Asset Library"; + ot->idname = "PREFERENCES_OT_asset_library_add"; + ot->description = + "Add a path to a .blend file to be used by the Asset Browser as source of assets"; + + ot->exec = preferences_asset_library_add_exec; + + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Remove Asset Library Operator + * \{ */ + +static int preferences_asset_library_remove_exec(bContext *UNUSED(C), wmOperator *op) +{ + const int index = RNA_int_get(op->ptr, "index"); + bUserAssetLibrary *library = BLI_findlink(&U.asset_libraries, index); + if (library) { + BKE_preferences_asset_library_remove(&U, library); + U.runtime.is_dirty = true; + /* Trigger refresh for the Asset Browser. */ + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL); + } + return OPERATOR_FINISHED; +} + +static void PREFERENCES_OT_asset_library_remove(wmOperatorType *ot) +{ + ot->name = "Remove Asset Library"; + ot->idname = "PREFERENCES_OT_asset_library_remove"; + ot->description = + "Remove a path to a .blend file, so the Asset Browser will not attempt to show it anymore"; + + ot->exec = preferences_asset_library_remove_exec; + + ot->flag = OPTYPE_INTERNAL; + + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000); +} + +/** \} */ + void ED_operatortypes_userpref(void) { WM_operatortype_append(PREFERENCES_OT_reset_default_theme); + WM_operatortype_append(PREFERENCES_OT_autoexec_path_add); WM_operatortype_append(PREFERENCES_OT_autoexec_path_remove); + + WM_operatortype_append(PREFERENCES_OT_asset_library_add); + WM_operatortype_append(PREFERENCES_OT_asset_library_remove); } diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index 0371b4e271f..9242fc15021 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -22,13 +22,13 @@ set(INC ../../blenlib ../../blentranslation ../../bmesh + ../../depsgraph ../../draw ../../gpu ../../imbuf ../../makesdna ../../makesrna ../../render - ../../depsgraph ../../windowmanager ../../../../intern/glew-mx ../../../../intern/guardedalloc diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 9f31e7a411d..3761f4ad7c6 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -461,6 +461,12 @@ static void view3d_main_region_exit(wmWindowManager *wm, ARegion *region) ED_view3d_stop_render_preview(wm, region); } +static bool view3d_drop_in_main_region_poll(bContext *C, const wmEvent *event) +{ + ScrArea *area = CTX_wm_area(C); + return ED_region_overlap_isect_any_xy(area, &event->x) == false; +} + static ID *view3d_drop_id_in_main_region_poll_id(bContext *C, wmDrag *drag, const wmEvent *event, @@ -470,7 +476,7 @@ static ID *view3d_drop_id_in_main_region_poll_id(bContext *C, if (ED_region_overlap_isect_any_xy(area, &event->x)) { return NULL; } - return WM_drag_ID(drag, id_type); + return view3d_drop_in_main_region_poll(C, event) ? WM_drag_get_local_ID(drag, id_type) : NULL; } static bool view3d_drop_id_in_main_region_poll(bContext *C, @@ -478,7 +484,11 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C, const wmEvent *event, ID_Type id_type) { - return (view3d_drop_id_in_main_region_poll_id(C, drag, event, id_type) != NULL); + if (!view3d_drop_in_main_region_poll(C, event)) { + return false; + } + + return WM_drag_get_local_ID(drag, id_type) || WM_drag_get_asset_data(drag, id_type); } static bool view3d_ob_drop_poll(bContext *C, @@ -533,7 +543,7 @@ static bool view3d_ima_drop_poll(bContext *C, return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE)); } - return WM_drag_ID(drag, ID_IM) != NULL; + return WM_drag_get_local_ID(drag, ID_IM) || WM_drag_get_asset_data(drag, ID_IM); } static bool view3d_ima_bg_is_camera_view(bContext *C) @@ -596,14 +606,14 @@ static bool view3d_volume_drop_poll(bContext *UNUSED(C), static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_ID(drag, ID_OB); + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB); RNA_string_set(drop->ptr, "name", id->name + 2); } static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_ID(drag, ID_GR); + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_GR); drop->opcontext = WM_OP_EXEC_DEFAULT; RNA_string_set(drop->ptr, "name", id->name + 2); @@ -611,14 +621,14 @@ static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop) static void view3d_id_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_ID(drag, 0); + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); RNA_string_set(drop->ptr, "name", id->name + 2); } static void view3d_id_drop_copy_with_type(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_ID(drag, 0); + ID *id = WM_drag_get_local_ID(drag, 0); RNA_string_set(drop->ptr, "name", id->name + 2); RNA_enum_set(drop->ptr, "type", GS(id->name)); @@ -626,7 +636,7 @@ static void view3d_id_drop_copy_with_type(wmDrag *drag, wmDropBox *drop) static void view3d_id_path_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_ID(drag, 0); + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); if (id) { RNA_string_set(drop->ptr, "name", id->name + 2); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 5b400bbf60a..96bd25f85e7 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2457,13 +2457,16 @@ static int view3d_select_exec(bContext *C, wmOperator *op) else if (obact && BKE_paint_select_face_test(obact)) { retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle); if (!retval && deselect_all) { - retval = paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, false); + retval = paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, true); } } else if (BKE_paint_select_vert_test(obact)) { retval = ed_wpaint_vertex_select_pick(C, location, extend, deselect, toggle, obact); if (!retval && deselect_all) { retval = paintvert_deselect_all_visible(obact, SEL_DESELECT, false); + if (retval) { + paintvert_tag_select_update(C, obact); + } } } else { diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 1be9bd27c7a..2b7b8255068 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -1620,6 +1620,41 @@ void ED_view3d_to_object(const Depsgraph *depsgraph, BKE_object_apply_mat4_ex(ob, mat, ob_eval->parent, ob_eval->parentinv, true); } +bool ED_view3d_camera_to_view_selected(struct Main *bmain, + Depsgraph *depsgraph, + const Scene *scene, + Object *camera_ob) +{ + Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob); + float co[3]; /* the new location to apply */ + float scale; /* only for ortho cameras */ + + if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, co, &scale)) { + ObjectTfmProtectedChannels obtfm; + float obmat_new[4][4]; + + if ((camera_ob_eval->type == OB_CAMERA) && + (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) { + ((Camera *)camera_ob->data)->ortho_scale = scale; + } + + copy_m4_m4(obmat_new, camera_ob_eval->obmat); + copy_v3_v3(obmat_new[3], co); + + /* only touch location */ + BKE_object_tfm_protected_backup(camera_ob, &obtfm); + BKE_object_apply_mat4(camera_ob, obmat_new, true, true); + BKE_object_tfm_protected_restore(camera_ob, &obtfm, OB_LOCK_SCALE | OB_LOCK_ROT4D); + + /* notifiers */ + DEG_id_tag_update_ex(bmain, &camera_ob->id, ID_RECALC_TRANSFORM); + + return true; + } + + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index a24f59019f0..9d947384bf0 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -535,40 +535,18 @@ void VIEW3D_OT_camera_to_view(wmOperatorType *ot) * meant to take into account vertex/bone selection for eg. */ static int view3d_camera_to_view_selected_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); /* can be NULL */ Object *camera_ob = v3d ? v3d->camera : scene->camera; - Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob); - - float r_co[3]; /* the new location to apply */ - float r_scale; /* only for ortho cameras */ - if (camera_ob_eval == NULL) { + if (camera_ob == NULL) { BKE_report(op->reports, RPT_ERROR, "No active camera"); return OPERATOR_CANCELLED; } - /* this function does all the important stuff */ - if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, r_co, &r_scale)) { - ObjectTfmProtectedChannels obtfm; - float obmat_new[4][4]; - - if ((camera_ob_eval->type == OB_CAMERA) && - (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) { - ((Camera *)camera_ob->data)->ortho_scale = r_scale; - } - - copy_m4_m4(obmat_new, camera_ob_eval->obmat); - copy_v3_v3(obmat_new[3], r_co); - - /* only touch location */ - BKE_object_tfm_protected_backup(camera_ob, &obtfm); - BKE_object_apply_mat4(camera_ob, obmat_new, true, true); - BKE_object_tfm_protected_restore(camera_ob, &obtfm, OB_LOCK_SCALE | OB_LOCK_ROT4D); - - /* notifiers */ - DEG_id_tag_update(&camera_ob->id, ID_RECALC_TRANSFORM); + if (ED_view3d_camera_to_view_selected(bmain, depsgraph, scene, camera_ob)) { WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, camera_ob); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index 73d6a376da6..a2eb68a1b71 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -22,12 +22,12 @@ set(INC ../../blenlib ../../blentranslation ../../bmesh + ../../depsgraph ../../gpu ../../ikplugin ../../makesdna ../../makesrna ../../render - ../../depsgraph ../../sequencer ../../windowmanager ../../../../intern/glew-mx diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 227330e8524..91bf2bf8aac 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -52,12 +52,12 @@ struct SnapObjectContext; struct TransDataContainer; struct TransInfo; struct TransSnap; -struct TransformOrientation; struct ViewLayer; struct bContext; struct wmEvent; struct wmKeyConfig; struct wmKeyMap; +struct wmOperator; struct wmTimer; /** #TransInfo.redraw */ diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index b753572ea7b..59fcd016020 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -29,12 +29,9 @@ struct FCurve; struct ListBase; struct Object; struct TransData; -struct TransDataContainer; struct TransDataCurveHandleFlags; struct TransInfo; struct bContext; -struct bKinematicConstraint; -struct bPoseChannel; /* transform_convert.c */ void transform_autoik_update(TransInfo *t, short mode); diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 90c1f241338..8597c372537 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -377,6 +377,9 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) else { mul_v3_m3v3(global_dir, t->spacemtx, global_dir); } + if (t->flag & T_2D_EDIT) { + removeAspectRatio(t, global_dir); + } } else { copy_v3_v3(global_dir, t->values); diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 5153fdedcae..5d758a6c6c6 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -717,7 +717,7 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) "use_automerge_and_split", 0, "Auto Merge & Split", - "Forces the use of Auto Merge & Split"); + "Forces the use of Auto Merge and Split"); RNA_def_property_flag(prop, PROP_HIDDEN); } } diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h index 5bee572c603..db8ec943bfd 100644 --- a/source/blender/editors/transform/transform_snap.h +++ b/source/blender/editors/transform/transform_snap.h @@ -25,8 +25,6 @@ /* For enum. */ #include "DNA_space_types.h" -struct SnapObjectParams; - bool peelObjectsTransform(struct TransInfo *t, const float mval[2], const bool use_peel_object, diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c index 49417a54472..4fded419b5b 100644 --- a/source/blender/editors/undo/memfile_undo.c +++ b/source/blender/editors/undo/memfile_undo.c @@ -27,6 +27,7 @@ #include "BLI_listbase.h" #include "DNA_ID.h" +#include "DNA_collection_types.h" #include "DNA_node_types.h" #include "DNA_object_enums.h" #include "DNA_object_types.h" diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index c88169778f7..bbaf5d5475a 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -47,6 +47,7 @@ set(SRC ../include/BIF_glutil.h ../include/ED_anim_api.h ../include/ED_armature.h + ../include/ED_asset.h ../include/ED_buttons.h ../include/ED_clip.h ../include/ED_curve.h diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 76c261c9cba..d78758dcc1c 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -35,6 +35,7 @@ #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "BLI_fileops.h" #include "BLI_listbase.h" #include "BLI_path_util.h" #include "BLI_string.h" @@ -44,6 +45,7 @@ #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_icons.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_material.h" @@ -51,6 +53,7 @@ #include "BKE_object.h" #include "BKE_packedFile.h" #include "BKE_paint.h" +#include "BKE_report.h" #include "BKE_screen.h" #include "BKE_undo_system.h" #include "BKE_workspace.h" @@ -64,6 +67,7 @@ #include "ED_object.h" #include "ED_outliner.h" #include "ED_paint.h" +#include "ED_render.h" #include "ED_space_api.h" #include "ED_util.h" @@ -501,3 +505,104 @@ void ED_OT_flush_edits(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_INTERNAL; } + +static bool lib_id_preview_editing_poll(bContext *C) +{ + const PointerRNA idptr = CTX_data_pointer_get(C, "id"); + BLI_assert(!idptr.data || RNA_struct_is_ID(idptr.type)); + + const ID *id = idptr.data; + if (!id) { + return false; + } + if (ID_IS_LINKED(id)) { + CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit external library data")); + return false; + } + if (ID_IS_OVERRIDE_LIBRARY(id)) { + CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit previews of overridden library data")); + return false; + } + if (!BKE_previewimg_id_get_p(id)) { + CTX_wm_operator_poll_msg_set(C, TIP_("Data-block does not support previews")); + return false; + } + + return true; +} + +static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op) +{ + char path[FILE_MAX]; + + RNA_string_get(op->ptr, "filepath", path); + + if (!BLI_is_file(path)) { + BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path); + return OPERATOR_CANCELLED; + } + + PointerRNA idptr = CTX_data_pointer_get(C, "id"); + ID *id = idptr.data; + + BKE_previewimg_id_custom_set(id, path); + + WM_event_add_notifier(C, NC_ASSET, NULL); + + return OPERATOR_FINISHED; +} + +void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot) +{ + ot->name = "Load Custom Preview"; + ot->description = "Choose an image to help identify the data-block visually"; + ot->idname = "ED_OT_lib_id_load_custom_preview"; + + /* api callbacks */ + ot->poll = lib_id_preview_editing_poll; + ot->exec = lib_id_load_custom_preview_exec; + ot->invoke = WM_operator_filesel; + + /* flags */ + ot->flag = OPTYPE_INTERNAL; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER | FILE_TYPE_IMAGE, + FILE_SPECIAL, + FILE_OPENFILE, + WM_FILESEL_FILEPATH, + FILE_DEFAULTDISPLAY, + FILE_SORT_DEFAULT); +} + +static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op)) +{ + PointerRNA idptr = CTX_data_pointer_get(C, "id"); + ID *id = idptr.data; + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + PreviewImage *preview = BKE_previewimg_id_get(id); + if (preview) { + BKE_previewimg_clear(preview); + } + UI_icon_render_id(C, NULL, id, true, true); + + WM_event_add_notifier(C, NC_ASSET, NULL); + + return OPERATOR_FINISHED; +} + +void ED_OT_lib_id_generate_preview(wmOperatorType *ot) +{ + ot->name = "Generate Preview"; + ot->description = "Create an automatic preview for the selected data-block"; + ot->idname = "ED_OT_lib_id_generate_preview"; + + /* api callbacks */ + ot->poll = lib_id_preview_editing_poll; + ot->exec = lib_id_generate_preview_exec; + + /* flags */ + ot->flag = OPTYPE_INTERNAL; +} diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt index 8e4a3df920e..1c8a56e0608 100644 --- a/source/blender/editors/uvedit/CMakeLists.txt +++ b/source/blender/editors/uvedit/CMakeLists.txt @@ -35,11 +35,11 @@ set(INC set(SRC uvedit_buttons.c uvedit_draw.c + uvedit_islands.c uvedit_ops.c uvedit_parametrizer.c uvedit_path.c uvedit_rip.c - uvedit_islands.c uvedit_select.c uvedit_smart_stitch.c uvedit_unwrap_ops.c diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 306f8a2c561..28567234fab 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -25,7 +25,6 @@ struct BMFace; struct BMLoop; -struct Image; struct Object; struct Scene; struct SpaceImage; diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 5b3d858329a..aac5b96f737 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -1812,7 +1812,7 @@ static void UV_OT_cursor_set(wmOperatorType *ot) -FLT_MAX, FLT_MAX, "Location", - "Cursor location in normalized (0.0-1.0) coordinates", + "Cursor location in normalized (0.0 to 1.0) coordinates", -10.0f, 10.0f); } diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 6dac31794b4..c1d222c9368 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -2808,7 +2808,7 @@ void UV_OT_stitch(wmOperatorType *ot) RNA_def_boolean(ot->srna, "midpoint_snap", 0, - "Snap At Midpoint", + "Snap at Midpoint", "UVs are stitched at midpoint instead of at static island"); RNA_def_boolean(ot->srna, "clear_seams", 1, "Clear Seams", "Clear seams of stitched edges"); RNA_def_enum(ot->srna, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c index 37cde3162a0..d3ba5bf8b37 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c @@ -28,6 +28,7 @@ #include "BKE_object.h" #include "BKE_screen.h" +#include "DNA_material_types.h" #include "DNA_object_force_types.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index 077a454db73..f5bfe66562b 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -33,6 +33,7 @@ #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h index e5a6d9e6a8f..30e54f44499 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h @@ -23,9 +23,7 @@ #pragma once -struct GHash; struct MDeformVert; -struct Main; struct Material; struct Object; struct bGPDlayer; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c index aa21bf192c4..b8fa88327fc 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c @@ -237,9 +237,17 @@ static void generate_geometry(GpencilModifierData *md, /* To ensure a nice distribution, we use halton sequence and offset using the seed. */ BLI_halton_3d(primes, offset, x, r); - for (int i = 0; i < 3; i++) { - rand[j][i] = fmodf(r[i] * 2.0 - 1.0 + rand_offset, 1.0f); - rand[j][i] = fmodf(sin(rand[j][i] * 12.9898 + j * 78.233) * 43758.5453, 1.0f); + if ((mmd->flag & GP_ARRAY_UNIFORM_RANDOM_SCALE) && j == 2) { + float rand_value; + rand_value = fmodf(r[0] * 2.0 - 1.0 + rand_offset, 1.0f); + rand_value = fmodf(sin(rand_value * 12.9898 + j * 78.233) * 43758.5453, 1.0f); + copy_v3_fl(rand[j], rand_value); + } + else { + for (int i = 0; i < 3; i++) { + rand[j][i] = fmodf(r[i] * 2.0 - 1.0 + rand_offset, 1.0f); + rand[j][i] = fmodf(sin(rand[j][i] * 12.9898 + j * 78.233) * 43758.5453, 1.0f); + } } } /* Calculate Random matrix. */ @@ -425,6 +433,7 @@ static void random_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "random_offset", 0, IFACE_("Offset"), ICON_NONE); uiItemR(layout, ptr, "random_rotation", 0, IFACE_("Rotation"), ICON_NONE); uiItemR(layout, ptr, "random_scale", 0, IFACE_("Scale"), ICON_NONE); + uiItemR(layout, ptr, "use_uniform_random_scale", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "seed", 0, NULL, ICON_NONE); } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c index 4e569099461..04405fed7d9 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c @@ -34,6 +34,7 @@ #include "DNA_defaults.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c index 2ee148837cd..311d08238b9 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -33,6 +33,7 @@ #include "DNA_defaults.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 27a7ea1e6a5..55716b584c3 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -27,8 +27,6 @@ extern "C" { #endif -struct GPUTexture; -struct GPUUniformBuf; struct GPUVertBuf; /** Opaque type hidding blender::gpu::Shader */ diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index e9c081abd22..91119bd05a1 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -28,14 +28,6 @@ #include "GPU_state.h" struct GPUVertBuf; -struct ImBuf; -struct Image; -struct ImageUser; -struct MovieClip; -struct MovieClipUser; -struct PreviewImage; - -struct GPUFrameBuffer; /** Opaque type hiding blender::gpu::Texture. */ typedef struct GPUTexture GPUTexture; diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index 5d130d75d2c..f3d58a55814 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -31,10 +31,7 @@ extern "C" { struct GPUMaterial; struct GPUNodeGraph; -struct GPUOutput; struct GPUShader; -struct GSet; -struct ListBase; typedef struct GPUPass { struct GPUPass *next; diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 0ef95d94c0d..929191cff73 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -33,8 +33,6 @@ struct GPUNode; struct GPUOutput; -struct GPUShader; -struct GPUVertAttrLayers; struct ListBase; typedef enum eGPUDataSource { diff --git a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh index 00d10776864..18ea6d18caf 100644 --- a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh @@ -24,6 +24,8 @@ #include "BLI_sys_types.h" +struct GPUUniformBuf; + namespace blender { namespace gpu { diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index 188c8786665..a83654d31e7 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -616,8 +616,8 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo GPU_framebuffer_ensure_config(&dfbl->stereo_comp_fb, { GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(dtxl->color), GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), + GPU_ATTACHMENT_TEXTURE(dtxl->color), }); GPUVertFormat *vert_format = immVertexFormat(); @@ -628,8 +628,8 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo GPU_matrix_identity_set(); GPU_matrix_identity_projection_set(); immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE); - immUniform1i("imageTexture", 0); - immUniform1i("overlayTexture", 1); + immUniform1i("overlayTexture", 0); + immUniform1i("imageTexture", 1); int settings = stereo_format->display_mode; if (settings == S3D_DISPLAY_ANAGLYPH) { switch (stereo_format->anaglyph_type) { diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 957352595ed..58ddc918f61 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -382,6 +382,8 @@ bool IMB_anim_can_produce_frames(const struct anim *anim); int ismovie(const char *filepath); void IMB_anim_set_preseek(struct anim *anim, int preseek); int IMB_anim_get_preseek(struct anim *anim); +int IMB_anim_get_image_width(struct anim *anim); +int IMB_anim_get_image_height(struct anim *anim); /** * diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index d825b20f5f2..9b01ea0840f 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -1506,3 +1506,13 @@ int IMB_anim_get_preseek(struct anim *anim) { return anim->preseek; } + +int IMB_anim_get_image_width(struct anim *anim) +{ + return anim->x; +} + +int IMB_anim_get_image_height(struct anim *anim) +{ + return anim->y; +} diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 93cdbbb1407..7d4797def8f 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -33,6 +33,8 @@ #include "BKE_idprop.h" +#include "DNA_ID.h" /* ID property definitions. */ + #include "IMB_filetype.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" diff --git a/source/blender/imbuf/intern/metadata.c b/source/blender/imbuf/intern/metadata.c index 28f09c38a96..d8abd3411cb 100644 --- a/source/blender/imbuf/intern/metadata.c +++ b/source/blender/imbuf/intern/metadata.c @@ -29,6 +29,8 @@ #include "BKE_idprop.h" +#include "DNA_ID.h" /* ID property definitions. */ + #include "MEM_guardedalloc.h" #include "IMB_imbuf.h" diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c index 60fc2ac0867..561a833803d 100644 --- a/source/blender/imbuf/intern/png.c +++ b/source/blender/imbuf/intern/png.c @@ -32,6 +32,8 @@ #include "BKE_global.h" #include "BKE_idprop.h" +#include "DNA_ID.h" /* ID property definitions. */ + #include "MEM_guardedalloc.h" #include "IMB_imbuf.h" diff --git a/source/blender/imbuf/intern/thumbs_blend.c b/source/blender/imbuf/intern/thumbs_blend.c index d5ded02be62..486db07597f 100644 --- a/source/blender/imbuf/intern/thumbs_blend.c +++ b/source/blender/imbuf/intern/thumbs_blend.c @@ -78,15 +78,7 @@ ImBuf *IMB_thumb_load_blend(const char *blen_path, const char *blen_group, const if (STREQ(blockname, blen_id)) { if (img) { - unsigned int w = img->w[ICON_SIZE_PREVIEW]; - unsigned int h = img->h[ICON_SIZE_PREVIEW]; - unsigned int *rect = img->rect[ICON_SIZE_PREVIEW]; - - if (w > 0 && h > 0 && rect) { - /* first allocate imbuf for copying preview into it */ - ima = IMB_allocImBuf(w, h, 32, IB_rect); - memcpy(ima->rect, rect, w * h * sizeof(unsigned int)); - } + ima = BKE_previewimg_to_imbuf(img, ICON_SIZE_PREVIEW); } break; } diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h index a0d9257b822..2098b8b2505 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h @@ -29,7 +29,6 @@ #include <Alembic/Abc/OObject.h> struct Depsgraph; -struct ID; struct Object; namespace blender::io::alembic { diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h index d23e69cf73e..4997e498199 100644 --- a/source/blender/io/alembic/exporter/abc_writer_abstract.h +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h @@ -29,7 +29,6 @@ #include "DNA_material_types.h" struct IDProperty; -struct Material; struct Object; namespace blender::io::alembic { diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc index f8d610a3659..80f2cadd08c 100644 --- a/source/blender/io/alembic/exporter/abc_writer_hair.cc +++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc @@ -30,6 +30,7 @@ #include "BLI_math_geom.h" +#include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_particle.h" diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h index 3759ffa4310..901fd70601f 100644 --- a/source/blender/io/alembic/exporter/abc_writer_hair.h +++ b/source/blender/io/alembic/exporter/abc_writer_hair.h @@ -23,9 +23,6 @@ #include <Alembic/AbcGeom/OCurves.h> #include <vector> -struct ParticleSettings; -struct ParticleSystem; - namespace blender::io::alembic { class ABCHairWriter : public ABCAbstractWriter { diff --git a/source/blender/io/alembic/intern/abc_reader_archive.h b/source/blender/io/alembic/intern/abc_reader_archive.h index 2a4fd6bd8fb..67000194aa1 100644 --- a/source/blender/io/alembic/intern/abc_reader_archive.h +++ b/source/blender/io/alembic/intern/abc_reader_archive.h @@ -28,7 +28,6 @@ #include <fstream> struct Main; -struct Scene; namespace blender::io::alembic { diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index c9eefbf9039..ad5a258f80c 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -35,6 +35,7 @@ #include "MEM_guardedalloc.h" #include "DNA_cachefile_types.h" +#include "DNA_collection_types.h" #include "DNA_curve_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp index 77ccdeae28d..9f54bf2aa28 100644 --- a/source/blender/io/collada/AnimationImporter.cpp +++ b/source/blender/io/collada/AnimationImporter.cpp @@ -385,7 +385,10 @@ virtual void AnimationImporter::change_eul_to_quat(Object *ob, bAction *act) char joint_path[100]; char rna_path[100]; - BLI_snprintf(joint_path, sizeof(joint_path), "pose.bones[\"%s\"]", grp->name); + char grp_name_esc[sizeof(grp->name) * 2]; + BLI_str_escape(grp_name_esc, grp->name, sizeof(grp_name_esc)); + + BLI_snprintf(joint_path, sizeof(joint_path), "pose.bones[\"%s\"]", grp_name_esc); BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation_quaternion", joint_path); FCurve *quatcu[4] = { diff --git a/source/blender/io/collada/ArmatureImporter.cpp b/source/blender/io/collada/ArmatureImporter.cpp index 9533ca322f9..7eef5c3f5fb 100644 --- a/source/blender/io/collada/ArmatureImporter.cpp +++ b/source/blender/io/collada/ArmatureImporter.cpp @@ -1037,7 +1037,9 @@ void ArmatureImporter::get_rna_path_for_joint(COLLADAFW::Node *node, char *joint_path, size_t count) { - BLI_snprintf(joint_path, count, "pose.bones[\"%s\"]", bc_get_joint_name(node)); + char bone_name_esc[sizeof(((Bone *)nullptr)->name) * 2]; + BLI_str_escape(bone_name_esc, bc_get_joint_name(node), sizeof(bone_name_esc)); + BLI_snprintf(joint_path, count, "pose.bones[\"%s\"]", bone_name_esc); } /* gives a world-space mat */ diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp index ad1cc1035fb..bba50064879 100644 --- a/source/blender/io/collada/collada_utils.cpp +++ b/source/blender/io/collada/collada_utils.cpp @@ -832,7 +832,9 @@ void bc_enable_fcurves(bAction *act, char *bone_name) char prefix[200]; if (bone_name) { - BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone_name); + char bone_name_esc[sizeof(((Bone *)nullptr)->name) * 2]; + BLI_str_escape(bone_name_esc, bone_name, sizeof(bone_name_esc)); + BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone_name_esc); } for (fcu = (FCurve *)act->curves.first; fcu; fcu = fcu->next) { diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index e066ca8ba8f..300c555ac8f 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -43,13 +43,11 @@ #include <set> #include <string> -struct Base; struct Depsgraph; struct DupliObject; struct ID; struct Object; struct ParticleSystem; -struct ViewLayer; namespace blender::io { diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc index 8acdba416d3..457d68953ff 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc @@ -36,7 +36,7 @@ namespace { /* Mapping from ID.name to set of export hierarchy path. Duplicated objects can be exported * multiple times with different export paths, hence the set. */ -typedef std::map<std::string, std::set<std::string>> used_writers; +using used_writers = std::map<std::string, std::set<std::string>>; class TestHierarchyWriter : public AbstractHierarchyWriter { public: diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h index 41ff34b327e..66e87f0eed1 100644 --- a/source/blender/io/usd/intern/usd_exporter_context.h +++ b/source/blender/io/usd/intern/usd_exporter_context.h @@ -24,7 +24,6 @@ #include <pxr/usd/usd/common.h> struct Depsgraph; -struct Object; namespace blender::io::usd { diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h index 922ab761bd9..cd19becbb07 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h @@ -28,7 +28,6 @@ #include <pxr/usd/usd/timeCode.h> struct Depsgraph; -struct ID; struct Object; namespace blender::io::usd { diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h index 6a3b8d515dc..2143164e3dd 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.h +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -33,7 +33,6 @@ #include "DNA_material_types.h" struct Material; -struct Object; namespace blender::io::usd { diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index b9ea90736ff..40e2d0d8674 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -25,7 +25,6 @@ extern "C" { #endif -struct Scene; struct bContext; struct USDExportParams { diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index f2d860a2851..265baa16cc5 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -264,7 +264,12 @@ typedef struct IDOverrideLibrary { typedef struct ID { void *next, *prev; struct ID *newid; + struct Library *lib; + + /** If the ID is an asset, this pointer is set. Owning pointer. */ + struct AssetMetaData *asset_data; + /** MAX_ID_NAME. */ char name[66]; /** @@ -310,6 +315,7 @@ typedef struct ID { struct ID *orig_id; void *py_instance; + void *_pad1; } ID; /** @@ -354,6 +360,7 @@ enum eIconSizes { enum ePreviewImage_Flag { PRV_CHANGED = (1 << 0), PRV_USER_EDITED = (1 << 1), /* if user-edited, do not auto-update this anymore! */ + PRV_UNFINISHED = (1 << 2), /* The preview is not done rendering yet. */ }; /* for PreviewImage->tag */ @@ -502,6 +509,8 @@ typedef enum ID_Type { #define ID_IS_OVERRIDE_LIBRARY_TEMPLATE(_id) \ (((ID *)(_id))->override_library != NULL && ((ID *)(_id))->override_library->reference == NULL) +#define ID_IS_ASSET(_id) (((const ID *)(_id))->asset_data != NULL) + /* Check whether datablock type is covered by copy-on-write. */ #define ID_TYPE_IS_COW(_id_type) (!ELEM(_id_type, ID_BR, ID_PAL, ID_IM)) diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 17d41985f80..1eafa655195 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -612,9 +612,18 @@ typedef struct FCurve { char _pad[3]; /* RNA - data link */ - /** If applicable, the index of the RNA-array item to get. */ + /** + * When the RNA property from `rna_path` is an array, use this to access the array index. + * + * \note This may be negative (as it wasn't prevented in 2.91 and older). + * Currently it silently fails to resolve the data-path in this case. + */ int array_index; - /** RNA-path to resolve data-access. */ + /** + * RNA-path to resolve data-access, see: #RNA_path_resolve_property. + * + * \note String look-ups for collection and custom-properties are escaped using #BLI_str_escape. + */ char *rna_path; /* curve coloring (for editor) */ diff --git a/source/blender/makesdna/DNA_asset_defaults.h b/source/blender/makesdna/DNA_asset_defaults.h new file mode 100644 index 00000000000..ff00ba79cf0 --- /dev/null +++ b/source/blender/makesdna/DNA_asset_defaults.h @@ -0,0 +1,37 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup DNA + */ + +#pragma once + +/* Struct members on own line. */ +/* clang-format off */ + +/* -------------------------------------------------------------------- */ +/** \name Asset Struct + * \{ */ + +#define _DNA_DEFAULT_AssetMetaData \ + { \ + 0 \ + } + +/** \} */ + +/* clang-format on */ diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h new file mode 100644 index 00000000000..671012e54ef --- /dev/null +++ b/source/blender/makesdna/DNA_asset_types.h @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup DNA + */ + +#ifndef __DNA_ASSET_TYPES_H__ +#define __DNA_ASSET_TYPES_H__ + +#include "DNA_listBase.h" + +/** + * \brief User defined tag. + * Currently only used by assets, could be used more often at some point. + * Maybe add a custom icon and color to these in future? + */ +typedef struct AssetTag { + struct AssetTag *next, *prev; + char name[64]; /* MAX_NAME */ +} AssetTag; + +/** + * \brief The meta-data of an asset. + * By creating and giving this for a data-block (#ID.asset_data), the data-block becomes an asset. + * + * \note This struct must be readable without having to read anything but blocks from the ID it is + * attached to! That way, asset information of a file can be read, without reading anything + * more than that from the file. So pointers to other IDs or ID data are strictly forbidden. + */ +typedef struct AssetMetaData { + /** Custom asset meta-data. Cannot store pointers to IDs (#STRUCT_NO_DATABLOCK_IDPROPERTIES)! */ + struct IDProperty *properties; + + /** Optional description of this asset for display in the UI. Dynamic length. */ + char *description; + /** User defined tags for this asset. The asset manager uses these for filtering, but how they + * function exactly (e.g. how they are registered to provide a list of searchable available tags) + * is up to the asset-engine. */ + ListBase tags; /* AssetTag */ + short active_tag; + /** Store the number of tags to avoid continuous counting. Could be turned into runtime data, we + * can always reliably reconstruct it from the list. */ + short tot_tags; + + char _pad[4]; +} AssetMetaData; + +#endif /* __DNA_ASSET_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h new file mode 100644 index 00000000000..f12934c9104 --- /dev/null +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -0,0 +1,614 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup DNA + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/* BrushGpencilSettings->preset_type. + * Use a range for each group and not continuous values.*/ +typedef enum eGPBrush_Presets { + GP_BRUSH_PRESET_UNKNOWN = 0, + + /* Draw 1-99. */ + GP_BRUSH_PRESET_AIRBRUSH = 1, + GP_BRUSH_PRESET_INK_PEN = 2, + GP_BRUSH_PRESET_INK_PEN_ROUGH = 3, + GP_BRUSH_PRESET_MARKER_BOLD = 4, + GP_BRUSH_PRESET_MARKER_CHISEL = 5, + GP_BRUSH_PRESET_PEN = 6, + GP_BRUSH_PRESET_PENCIL_SOFT = 7, + GP_BRUSH_PRESET_PENCIL = 8, + GP_BRUSH_PRESET_FILL_AREA = 9, + GP_BRUSH_PRESET_ERASER_SOFT = 10, + GP_BRUSH_PRESET_ERASER_HARD = 11, + GP_BRUSH_PRESET_ERASER_POINT = 12, + GP_BRUSH_PRESET_ERASER_STROKE = 13, + GP_BRUSH_PRESET_TINT = 14, + + /* Vertex Paint 100-199. */ + GP_BRUSH_PRESET_VERTEX_DRAW = 100, + GP_BRUSH_PRESET_VERTEX_BLUR = 101, + GP_BRUSH_PRESET_VERTEX_AVERAGE = 102, + GP_BRUSH_PRESET_VERTEX_SMEAR = 103, + GP_BRUSH_PRESET_VERTEX_REPLACE = 104, + + /* Sculpt 200-299. */ + GP_BRUSH_PRESET_SMOOTH_STROKE = 200, + GP_BRUSH_PRESET_STRENGTH_STROKE = 201, + GP_BRUSH_PRESET_THICKNESS_STROKE = 202, + GP_BRUSH_PRESET_GRAB_STROKE = 203, + GP_BRUSH_PRESET_PUSH_STROKE = 204, + GP_BRUSH_PRESET_TWIST_STROKE = 205, + GP_BRUSH_PRESET_PINCH_STROKE = 206, + GP_BRUSH_PRESET_RANDOMIZE_STROKE = 207, + GP_BRUSH_PRESET_CLONE_STROKE = 208, + + /* Weight Paint 300-399. */ + GP_BRUSH_PRESET_DRAW_WEIGHT = 300, +} eGPBrush_Presets; + +/* BrushGpencilSettings->flag */ +typedef enum eGPDbrush_Flag { + /* brush use pressure */ + GP_BRUSH_USE_PRESSURE = (1 << 0), + /* brush use pressure for alpha factor */ + GP_BRUSH_USE_STRENGTH_PRESSURE = (1 << 1), + /* brush use pressure for alpha factor */ + GP_BRUSH_USE_JITTER_PRESSURE = (1 << 2), + /* fill hide transparent */ + GP_BRUSH_FILL_HIDE = (1 << 6), + /* show fill help lines */ + GP_BRUSH_FILL_SHOW_HELPLINES = (1 << 7), + /* lazy mouse */ + GP_BRUSH_STABILIZE_MOUSE = (1 << 8), + /* lazy mouse override (internal only) */ + GP_BRUSH_STABILIZE_MOUSE_TEMP = (1 << 9), + /* default eraser brush for quick switch */ + GP_BRUSH_DEFAULT_ERASER = (1 << 10), + /* settings group */ + GP_BRUSH_GROUP_SETTINGS = (1 << 11), + /* Random settings group */ + GP_BRUSH_GROUP_RANDOM = (1 << 12), + /* Keep material assigned to brush */ + GP_BRUSH_MATERIAL_PINNED = (1 << 13), + /* Do not show fill color while drawing (no lasso mode) */ + GP_BRUSH_DISSABLE_LASSO = (1 << 14), + /* Do not erase strokes oLcluded */ + GP_BRUSH_OCCLUDE_ERASER = (1 << 15), + /* Post process trim stroke */ + GP_BRUSH_TRIM_STROKE = (1 << 16), +} eGPDbrush_Flag; + +typedef enum eGPDbrush_Flag2 { + /* Brush use random Hue at stroke level */ + GP_BRUSH_USE_HUE_AT_STROKE = (1 << 0), + /* Brush use random Saturation at stroke level */ + GP_BRUSH_USE_SAT_AT_STROKE = (1 << 1), + /* Brush use random Value at stroke level */ + GP_BRUSH_USE_VAL_AT_STROKE = (1 << 2), + /* Brush use random Pressure at stroke level */ + GP_BRUSH_USE_PRESS_AT_STROKE = (1 << 3), + /* Brush use random Strength at stroke level */ + GP_BRUSH_USE_STRENGTH_AT_STROKE = (1 << 4), + /* Brush use random UV at stroke level */ + GP_BRUSH_USE_UV_AT_STROKE = (1 << 5), + /* Brush use Hue random pressure */ + GP_BRUSH_USE_HUE_RAND_PRESS = (1 << 6), + /* Brush use Saturation random pressure */ + GP_BRUSH_USE_SAT_RAND_PRESS = (1 << 7), + /* Brush use Value random pressure */ + GP_BRUSH_USE_VAL_RAND_PRESS = (1 << 8), + /* Brush use Pressure random pressure */ + GP_BRUSH_USE_PRESSURE_RAND_PRESS = (1 << 9), + /* Brush use Strength random pressure */ + GP_BRUSH_USE_STRENGTH_RAND_PRESS = (1 << 10), + /* Brush use UV random pressure */ + GP_BRUSH_USE_UV_RAND_PRESS = (1 << 11), +} eGPDbrush_Flag2; + +/* BrushGpencilSettings->gp_fill_draw_mode */ +typedef enum eGP_FillDrawModes { + GP_FILL_DMODE_BOTH = 0, + GP_FILL_DMODE_STROKE = 1, + GP_FILL_DMODE_CONTROL = 2, +} eGP_FillDrawModes; + +/* BrushGpencilSettings->fill_layer_mode */ +typedef enum eGP_FillLayerModes { + GP_FILL_GPLMODE_VISIBLE = 0, + GP_FILL_GPLMODE_ACTIVE = 1, + GP_FILL_GPLMODE_ALL_ABOVE = 2, + GP_FILL_GPLMODE_ALL_BELOW = 3, + GP_FILL_GPLMODE_ABOVE = 4, + GP_FILL_GPLMODE_BELOW = 5, +} eGP_FillLayerModes; + +/* BrushGpencilSettings->gp_eraser_mode */ +typedef enum eGP_BrushEraserMode { + GP_BRUSH_ERASER_SOFT = 0, + GP_BRUSH_ERASER_HARD = 1, + GP_BRUSH_ERASER_STROKE = 2, +} eGP_BrushEraserMode; + +/* BrushGpencilSettings->brush_draw_mode */ +typedef enum eGP_BrushMode { + GP_BRUSH_MODE_ACTIVE = 0, + GP_BRUSH_MODE_MATERIAL = 1, + GP_BRUSH_MODE_VERTEXCOLOR = 2, +} eGP_BrushMode; + +/* BrushGpencilSettings default brush icons */ +typedef enum eGP_BrushIcons { + GP_BRUSH_ICON_PENCIL = 1, + GP_BRUSH_ICON_PEN = 2, + GP_BRUSH_ICON_INK = 3, + GP_BRUSH_ICON_INKNOISE = 4, + GP_BRUSH_ICON_BLOCK = 5, + GP_BRUSH_ICON_MARKER = 6, + GP_BRUSH_ICON_FILL = 7, + GP_BRUSH_ICON_ERASE_SOFT = 8, + GP_BRUSH_ICON_ERASE_HARD = 9, + GP_BRUSH_ICON_ERASE_STROKE = 10, + GP_BRUSH_ICON_AIRBRUSH = 11, + GP_BRUSH_ICON_CHISEL = 12, + GP_BRUSH_ICON_TINT = 13, + GP_BRUSH_ICON_VERTEX_DRAW = 14, + GP_BRUSH_ICON_VERTEX_BLUR = 15, + GP_BRUSH_ICON_VERTEX_AVERAGE = 16, + GP_BRUSH_ICON_VERTEX_SMEAR = 17, + GP_BRUSH_ICON_VERTEX_REPLACE = 18, + GP_BRUSH_ICON_GPBRUSH_SMOOTH = 19, + GP_BRUSH_ICON_GPBRUSH_THICKNESS = 20, + GP_BRUSH_ICON_GPBRUSH_STRENGTH = 21, + GP_BRUSH_ICON_GPBRUSH_RANDOMIZE = 22, + GP_BRUSH_ICON_GPBRUSH_GRAB = 23, + GP_BRUSH_ICON_GPBRUSH_PUSH = 24, + GP_BRUSH_ICON_GPBRUSH_TWIST = 25, + GP_BRUSH_ICON_GPBRUSH_PINCH = 26, + GP_BRUSH_ICON_GPBRUSH_CLONE = 27, + GP_BRUSH_ICON_GPBRUSH_WEIGHT = 28, +} eGP_BrushIcons; + +typedef enum eBrushCurvePreset { + BRUSH_CURVE_CUSTOM = 0, + BRUSH_CURVE_SMOOTH = 1, + BRUSH_CURVE_SPHERE = 2, + BRUSH_CURVE_ROOT = 3, + BRUSH_CURVE_SHARP = 4, + BRUSH_CURVE_LIN = 5, + BRUSH_CURVE_POW4 = 6, + BRUSH_CURVE_INVSQUARE = 7, + BRUSH_CURVE_CONSTANT = 8, + BRUSH_CURVE_SMOOTHER = 9, +} eBrushCurvePreset; + +typedef enum eBrushDeformTarget { + BRUSH_DEFORM_TARGET_GEOMETRY = 0, + BRUSH_DEFORM_TARGET_CLOTH_SIM = 1, +} eBrushDeformTarget; + +typedef enum eBrushElasticDeformType { + BRUSH_ELASTIC_DEFORM_GRAB = 0, + BRUSH_ELASTIC_DEFORM_GRAB_BISCALE = 1, + BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE = 2, + BRUSH_ELASTIC_DEFORM_SCALE = 3, + BRUSH_ELASTIC_DEFORM_TWIST = 4, +} eBrushElasticDeformType; + +typedef enum eBrushClothDeformType { + BRUSH_CLOTH_DEFORM_DRAG = 0, + BRUSH_CLOTH_DEFORM_PUSH = 1, + BRUSH_CLOTH_DEFORM_GRAB = 2, + BRUSH_CLOTH_DEFORM_PINCH_POINT = 3, + BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR = 4, + BRUSH_CLOTH_DEFORM_INFLATE = 5, + BRUSH_CLOTH_DEFORM_EXPAND = 6, + BRUSH_CLOTH_DEFORM_SNAKE_HOOK = 7, +} eBrushClothDeformType; + +typedef enum eBrushSmoothDeformType { + BRUSH_SMOOTH_DEFORM_LAPLACIAN = 0, + BRUSH_SMOOTH_DEFORM_SURFACE = 1, +} eBrushSmoothDeformType; + +typedef enum eBrushClothForceFalloffType { + BRUSH_CLOTH_FORCE_FALLOFF_RADIAL = 0, + BRUSH_CLOTH_FORCE_FALLOFF_PLANE = 1, +} eBrushClothForceFalloffType; + +typedef enum eBrushClothSimulationAreaType { + BRUSH_CLOTH_SIMULATION_AREA_LOCAL = 0, + BRUSH_CLOTH_SIMULATION_AREA_GLOBAL = 1, + BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC = 2, +} eBrushClothSimulationAreaType; + +typedef enum eBrushPoseDeformType { + BRUSH_POSE_DEFORM_ROTATE_TWIST = 0, + BRUSH_POSE_DEFORM_SCALE_TRASLATE = 1, + BRUSH_POSE_DEFORM_SQUASH_STRETCH = 2, +} eBrushPoseDeformType; + +typedef enum eBrushPoseOriginType { + BRUSH_POSE_ORIGIN_TOPOLOGY = 0, + BRUSH_POSE_ORIGIN_FACE_SETS = 1, + BRUSH_POSE_ORIGIN_FACE_SETS_FK = 2, +} eBrushPoseOriginType; + +typedef enum eBrushSmearDeformType { + BRUSH_SMEAR_DEFORM_DRAG = 0, + BRUSH_SMEAR_DEFORM_PINCH = 1, + BRUSH_SMEAR_DEFORM_EXPAND = 2, +} eBrushSmearDeformType; + +typedef enum eBrushSlideDeformType { + BRUSH_SLIDE_DEFORM_DRAG = 0, + BRUSH_SLIDE_DEFORM_PINCH = 1, + BRUSH_SLIDE_DEFORM_EXPAND = 2, +} eBrushSlideDeformType; + +typedef enum eBrushBoundaryDeformType { + BRUSH_BOUNDARY_DEFORM_BEND = 0, + BRUSH_BOUNDARY_DEFORM_EXPAND = 1, + BRUSH_BOUNDARY_DEFORM_INFLATE = 2, + BRUSH_BOUNDARY_DEFORM_GRAB = 3, + BRUSH_BOUNDARY_DEFORM_TWIST = 4, + BRUSH_BOUNDARY_DEFORM_SMOOTH = 5, +} eBrushBushBoundaryDeformType; + +typedef enum eBrushBoundaryFalloffType { + BRUSH_BOUNDARY_FALLOFF_CONSTANT = 0, + BRUSH_BOUNDARY_FALLOFF_RADIUS = 1, + BRUSH_BOUNDARY_FALLOFF_LOOP = 2, + BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT = 3, +} eBrushBoundaryFalloffType; + +typedef enum eBrushSnakeHookDeformType { + BRUSH_SNAKE_HOOK_DEFORM_FALLOFF = 0, + BRUSH_SNAKE_HOOK_DEFORM_ELASTIC = 1, +} eBrushSnakeHookDeformType; + +/* Gpencilsettings.Vertex_mode */ +typedef enum eGp_Vertex_Mode { + /* Affect to Stroke only. */ + GPPAINT_MODE_STROKE = 0, + /* Affect to Fill only. */ + GPPAINT_MODE_FILL = 1, + /* Affect to both. */ + GPPAINT_MODE_BOTH = 2, +} eGp_Vertex_Mode; + +/* sculpt_flag */ +typedef enum eGP_Sculpt_Flag { + /* invert the effect of the brush */ + GP_SCULPT_FLAG_INVERT = (1 << 0), + /* smooth brush affects pressure values as well */ + GP_SCULPT_FLAG_SMOOTH_PRESSURE = (1 << 2), + /* temporary invert action */ + GP_SCULPT_FLAG_TMP_INVERT = (1 << 3), +} eGP_Sculpt_Flag; + +/* sculpt_mode_flag */ +typedef enum eGP_Sculpt_Mode_Flag { + /* apply brush to position */ + GP_SCULPT_FLAGMODE_APPLY_POSITION = (1 << 0), + /* apply brush to strength */ + GP_SCULPT_FLAGMODE_APPLY_STRENGTH = (1 << 1), + /* apply brush to thickness */ + GP_SCULPT_FLAGMODE_APPLY_THICKNESS = (1 << 2), + /* apply brush to uv data */ + GP_SCULPT_FLAGMODE_APPLY_UV = (1 << 3), +} eGP_Sculpt_Mode_Flag; + +typedef enum eAutomasking_flag { + BRUSH_AUTOMASKING_TOPOLOGY = (1 << 0), + BRUSH_AUTOMASKING_FACE_SETS = (1 << 1), + BRUSH_AUTOMASKING_BOUNDARY_EDGES = (1 << 2), + BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS = (1 << 3), +} eAutomasking_flag; + +typedef enum ePaintBrush_flag { + BRUSH_PAINT_HARDNESS_PRESSURE = (1 << 0), + BRUSH_PAINT_HARDNESS_PRESSURE_INVERT = (1 << 1), + BRUSH_PAINT_FLOW_PRESSURE = (1 << 2), + BRUSH_PAINT_FLOW_PRESSURE_INVERT = (1 << 3), + BRUSH_PAINT_WET_MIX_PRESSURE = (1 << 4), + BRUSH_PAINT_WET_MIX_PRESSURE_INVERT = (1 << 5), + BRUSH_PAINT_WET_PERSISTENCE_PRESSURE = (1 << 6), + BRUSH_PAINT_WET_PERSISTENCE_PRESSURE_INVERT = (1 << 7), + BRUSH_PAINT_DENSITY_PRESSURE = (1 << 8), + BRUSH_PAINT_DENSITY_PRESSURE_INVERT = (1 << 9), +} ePaintBrush_flag; + +/* Brush.gradient_source */ +typedef enum eBrushGradientSourceStroke { + BRUSH_GRADIENT_PRESSURE = 0, /* gradient from pressure */ + BRUSH_GRADIENT_SPACING_REPEAT = 1, /* gradient from spacing */ + BRUSH_GRADIENT_SPACING_CLAMP = 2, /* gradient from spacing */ +} eBrushGradientSourceStroke; + +typedef enum eBrushGradientSourceFill { + BRUSH_GRADIENT_LINEAR = 0, /* gradient from pressure */ + BRUSH_GRADIENT_RADIAL = 1, /* gradient from spacing */ +} eBrushGradientSourceFill; + +/* Brush.flag */ +typedef enum eBrushFlags { + BRUSH_AIRBRUSH = (1 << 0), + BRUSH_INVERT_TO_SCRAPE_FILL = (1 << 1), + BRUSH_ALPHA_PRESSURE = (1 << 2), + BRUSH_SIZE_PRESSURE = (1 << 3), + BRUSH_JITTER_PRESSURE = (1 << 4), + BRUSH_SPACING_PRESSURE = (1 << 5), + BRUSH_ORIGINAL_PLANE = (1 << 6), + BRUSH_GRAB_ACTIVE_VERTEX = (1 << 7), + BRUSH_ANCHORED = (1 << 8), + BRUSH_DIR_IN = (1 << 9), + BRUSH_SPACE = (1 << 10), + BRUSH_SMOOTH_STROKE = (1 << 11), + BRUSH_PERSISTENT = (1 << 12), + BRUSH_ACCUMULATE = (1 << 13), + BRUSH_LOCK_ALPHA = (1 << 14), + BRUSH_ORIGINAL_NORMAL = (1 << 15), + BRUSH_OFFSET_PRESSURE = (1 << 16), + BRUSH_SCENE_SPACING = (1 << 17), + BRUSH_SPACE_ATTEN = (1 << 18), + BRUSH_ADAPTIVE_SPACE = (1 << 19), + BRUSH_LOCK_SIZE = (1 << 20), + BRUSH_USE_GRADIENT = (1 << 21), + BRUSH_EDGE_TO_EDGE = (1 << 22), + BRUSH_DRAG_DOT = (1 << 23), + BRUSH_INVERSE_SMOOTH_PRESSURE = (1 << 24), + BRUSH_FRONTFACE_FALLOFF = (1 << 25), + BRUSH_PLANE_TRIM = (1 << 26), + BRUSH_FRONTFACE = (1 << 27), + BRUSH_CUSTOM_ICON = (1 << 28), + BRUSH_LINE = (1 << 29), + BRUSH_ABSOLUTE_JITTER = (1 << 30), + BRUSH_CURVE = (1u << 31), +} eBrushFlags; + +/* Brush.sampling_flag */ +typedef enum eBrushSamplingFlags { + BRUSH_PAINT_ANTIALIASING = (1 << 0), +} eBrushSamplingFlags; + +/* Brush.flag2 */ +typedef enum eBrushFlags2 { + BRUSH_MULTIPLANE_SCRAPE_DYNAMIC = (1 << 0), + BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW = (1 << 1), + BRUSH_POSE_IK_ANCHORED = (1 << 2), + BRUSH_USE_CONNECTED_ONLY = (1 << 3), + BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY = (1 << 4), + BRUSH_POSE_USE_LOCK_ROTATION = (1 << 5), + BRUSH_CLOTH_USE_COLLISION = (1 << 6), + BRUSH_AREA_RADIUS_PRESSURE = (1 << 7), + BRUSH_GRAB_SILHOUETTE = (1 << 8), +} eBrushFlags2; + +typedef enum { + BRUSH_MASK_PRESSURE_RAMP = (1 << 1), + BRUSH_MASK_PRESSURE_CUTOFF = (1 << 2), +} BrushMaskPressureFlags; + +/* Brush.overlay_flags */ +typedef enum eOverlayFlags { + BRUSH_OVERLAY_CURSOR = (1), + BRUSH_OVERLAY_PRIMARY = (1 << 1), + BRUSH_OVERLAY_SECONDARY = (1 << 2), + BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE = (1 << 3), + BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE = (1 << 4), + BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE = (1 << 5), +} eOverlayFlags; + +#define BRUSH_OVERLAY_OVERRIDE_MASK \ + (BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE | BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE | \ + BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE) + +/* Brush.sculpt_tool */ +typedef enum eBrushSculptTool { + SCULPT_TOOL_DRAW = 1, + SCULPT_TOOL_SMOOTH = 2, + SCULPT_TOOL_PINCH = 3, + SCULPT_TOOL_INFLATE = 4, + SCULPT_TOOL_GRAB = 5, + SCULPT_TOOL_LAYER = 6, + SCULPT_TOOL_FLATTEN = 7, + SCULPT_TOOL_CLAY = 8, + SCULPT_TOOL_FILL = 9, + SCULPT_TOOL_SCRAPE = 10, + SCULPT_TOOL_NUDGE = 11, + SCULPT_TOOL_THUMB = 12, + SCULPT_TOOL_SNAKE_HOOK = 13, + SCULPT_TOOL_ROTATE = 14, + SCULPT_TOOL_SIMPLIFY = 15, + SCULPT_TOOL_CREASE = 16, + SCULPT_TOOL_BLOB = 17, + SCULPT_TOOL_CLAY_STRIPS = 18, + SCULPT_TOOL_MASK = 19, + SCULPT_TOOL_DRAW_SHARP = 20, + SCULPT_TOOL_ELASTIC_DEFORM = 21, + SCULPT_TOOL_POSE = 22, + SCULPT_TOOL_MULTIPLANE_SCRAPE = 23, + SCULPT_TOOL_SLIDE_RELAX = 24, + SCULPT_TOOL_CLAY_THUMB = 25, + SCULPT_TOOL_CLOTH = 26, + SCULPT_TOOL_DRAW_FACE_SETS = 27, + SCULPT_TOOL_PAINT = 28, + SCULPT_TOOL_SMEAR = 29, + SCULPT_TOOL_BOUNDARY = 30, + SCULPT_TOOL_DISPLACEMENT_ERASER = 31, + SCULPT_TOOL_DISPLACEMENT_SMEAR = 32, +} eBrushSculptTool; + +/* Brush.uv_sculpt_tool */ +typedef enum eBrushUVSculptTool { + UV_SCULPT_TOOL_GRAB = 0, + UV_SCULPT_TOOL_RELAX = 1, + UV_SCULPT_TOOL_PINCH = 2, +} eBrushUVSculptTool; + +/** When #BRUSH_ACCUMULATE is used */ +#define SCULPT_TOOL_HAS_ACCUMULATE(t) \ + ELEM(t, \ + SCULPT_TOOL_DRAW, \ + SCULPT_TOOL_DRAW_SHARP, \ + SCULPT_TOOL_SLIDE_RELAX, \ + SCULPT_TOOL_CREASE, \ + SCULPT_TOOL_BLOB, \ + SCULPT_TOOL_INFLATE, \ + SCULPT_TOOL_CLAY, \ + SCULPT_TOOL_CLAY_STRIPS, \ + SCULPT_TOOL_CLAY_THUMB, \ + SCULPT_TOOL_ROTATE, \ + SCULPT_TOOL_SCRAPE, \ + SCULPT_TOOL_FLATTEN) + +#define SCULPT_TOOL_HAS_NORMAL_WEIGHT(t) \ + ELEM(t, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_ELASTIC_DEFORM) + +#define SCULPT_TOOL_HAS_RAKE(t) ELEM(t, SCULPT_TOOL_SNAKE_HOOK) + +#define SCULPT_TOOL_HAS_DYNTOPO(t) \ + (ELEM(t, /* These brushes, as currently coded, cannot support dynamic topology */ \ + SCULPT_TOOL_GRAB, \ + SCULPT_TOOL_ROTATE, \ + SCULPT_TOOL_CLOTH, \ + SCULPT_TOOL_THUMB, \ + SCULPT_TOOL_LAYER, \ + SCULPT_TOOL_DISPLACEMENT_ERASER, \ + SCULPT_TOOL_DRAW_SHARP, \ + SCULPT_TOOL_SLIDE_RELAX, \ + SCULPT_TOOL_ELASTIC_DEFORM, \ + SCULPT_TOOL_BOUNDARY, \ + SCULPT_TOOL_POSE, \ + SCULPT_TOOL_DRAW_FACE_SETS, \ + SCULPT_TOOL_PAINT, \ + SCULPT_TOOL_SMEAR, \ +\ + /* These brushes could handle dynamic topology, \ \ + * but user feedback indicates it's better not to */ \ + SCULPT_TOOL_SMOOTH, \ + SCULPT_TOOL_MASK) == 0) + +#define SCULPT_TOOL_HAS_TOPOLOGY_RAKE(t) \ + (ELEM(t, /* These brushes, as currently coded, cannot support topology rake. */ \ + SCULPT_TOOL_GRAB, \ + SCULPT_TOOL_ROTATE, \ + SCULPT_TOOL_THUMB, \ + SCULPT_TOOL_DRAW_SHARP, \ + SCULPT_TOOL_DISPLACEMENT_ERASER, \ + SCULPT_TOOL_SLIDE_RELAX, \ + SCULPT_TOOL_MASK) == 0) + +/* ImagePaintSettings.tool */ +typedef enum eBrushImagePaintTool { + PAINT_TOOL_DRAW = 0, + PAINT_TOOL_SOFTEN = 1, + PAINT_TOOL_SMEAR = 2, + PAINT_TOOL_CLONE = 3, + PAINT_TOOL_FILL = 4, + PAINT_TOOL_MASK = 5, +} eBrushImagePaintTool; + +typedef enum eBrushVertexPaintTool { + VPAINT_TOOL_DRAW = 0, + VPAINT_TOOL_BLUR = 1, + VPAINT_TOOL_AVERAGE = 2, + VPAINT_TOOL_SMEAR = 3, +} eBrushVertexPaintTool; + +typedef enum eBrushWeightPaintTool { + WPAINT_TOOL_DRAW = 0, + WPAINT_TOOL_BLUR = 1, + WPAINT_TOOL_AVERAGE = 2, + WPAINT_TOOL_SMEAR = 3, +} eBrushWeightPaintTool; + +/* BrushGpencilSettings->brush type */ +typedef enum eBrushGPaintTool { + GPAINT_TOOL_DRAW = 0, + GPAINT_TOOL_FILL = 1, + GPAINT_TOOL_ERASE = 2, + GPAINT_TOOL_TINT = 3, +} eBrushGPaintTool; + +/* BrushGpencilSettings->brush type */ +typedef enum eBrushGPVertexTool { + GPVERTEX_TOOL_DRAW = 0, + GPVERTEX_TOOL_BLUR = 1, + GPVERTEX_TOOL_AVERAGE = 2, + GPVERTEX_TOOL_TINT = 3, + GPVERTEX_TOOL_SMEAR = 4, + GPVERTEX_TOOL_REPLACE = 5, +} eBrushGPVertexTool; + +/* BrushGpencilSettings->brush type */ +typedef enum eBrushGPSculptTool { + GPSCULPT_TOOL_SMOOTH = 0, + GPSCULPT_TOOL_THICKNESS = 1, + GPSCULPT_TOOL_STRENGTH = 2, + GPSCULPT_TOOL_GRAB = 3, + GPSCULPT_TOOL_PUSH = 4, + GPSCULPT_TOOL_TWIST = 5, + GPSCULPT_TOOL_PINCH = 6, + GPSCULPT_TOOL_RANDOMIZE = 7, + GPSCULPT_TOOL_CLONE = 8, +} eBrushGPSculptTool; + +/* BrushGpencilSettings->brush type */ +typedef enum eBrushGPWeightTool { + GPWEIGHT_TOOL_DRAW = 0, +} eBrushGPWeightTool; + +/* direction that the brush displaces along */ +enum { + SCULPT_DISP_DIR_AREA = 0, + SCULPT_DISP_DIR_VIEW = 1, + SCULPT_DISP_DIR_X = 2, + SCULPT_DISP_DIR_Y = 3, + SCULPT_DISP_DIR_Z = 4, +}; + +typedef enum { + BRUSH_MASK_DRAW = 0, + BRUSH_MASK_SMOOTH = 1, +} BrushMaskTool; + +/* blur kernel types, Brush.blur_mode */ +typedef enum eBlurKernelType { + KERNEL_GAUSSIAN = 0, + KERNEL_BOX = 1, +} eBlurKernelType; + +/* Brush.falloff_shape */ +enum { + PAINT_FALLOFF_SHAPE_SPHERE = 0, + PAINT_FALLOFF_SHAPE_TUBE = 1, +}; + +#define MAX_BRUSH_PIXEL_RADIUS 500 +#define GP_MAX_BRUSH_PIXEL_RADIUS 1000 + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 7bd3c7d0117..1709ea5dc63 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -24,6 +24,7 @@ #pragma once #include "DNA_ID.h" +#include "DNA_brush_enums.h" #include "DNA_curve_types.h" #include "DNA_texture_types.h" /* for MTex */ @@ -144,321 +145,6 @@ typedef struct BrushGpencilSettings { struct Material *material; } BrushGpencilSettings; -/* BrushGpencilSettings->preset_type. - * Use a range for each group and not continuous values.*/ -typedef enum eGPBrush_Presets { - GP_BRUSH_PRESET_UNKNOWN = 0, - - /* Draw 1-99. */ - GP_BRUSH_PRESET_AIRBRUSH = 1, - GP_BRUSH_PRESET_INK_PEN = 2, - GP_BRUSH_PRESET_INK_PEN_ROUGH = 3, - GP_BRUSH_PRESET_MARKER_BOLD = 4, - GP_BRUSH_PRESET_MARKER_CHISEL = 5, - GP_BRUSH_PRESET_PEN = 6, - GP_BRUSH_PRESET_PENCIL_SOFT = 7, - GP_BRUSH_PRESET_PENCIL = 8, - GP_BRUSH_PRESET_FILL_AREA = 9, - GP_BRUSH_PRESET_ERASER_SOFT = 10, - GP_BRUSH_PRESET_ERASER_HARD = 11, - GP_BRUSH_PRESET_ERASER_POINT = 12, - GP_BRUSH_PRESET_ERASER_STROKE = 13, - GP_BRUSH_PRESET_TINT = 14, - - /* Vertex Paint 100-199. */ - GP_BRUSH_PRESET_VERTEX_DRAW = 100, - GP_BRUSH_PRESET_VERTEX_BLUR = 101, - GP_BRUSH_PRESET_VERTEX_AVERAGE = 102, - GP_BRUSH_PRESET_VERTEX_SMEAR = 103, - GP_BRUSH_PRESET_VERTEX_REPLACE = 104, - - /* Sculpt 200-299. */ - GP_BRUSH_PRESET_SMOOTH_STROKE = 200, - GP_BRUSH_PRESET_STRENGTH_STROKE = 201, - GP_BRUSH_PRESET_THICKNESS_STROKE = 202, - GP_BRUSH_PRESET_GRAB_STROKE = 203, - GP_BRUSH_PRESET_PUSH_STROKE = 204, - GP_BRUSH_PRESET_TWIST_STROKE = 205, - GP_BRUSH_PRESET_PINCH_STROKE = 206, - GP_BRUSH_PRESET_RANDOMIZE_STROKE = 207, - GP_BRUSH_PRESET_CLONE_STROKE = 208, - - /* Weight Paint 300-399. */ - GP_BRUSH_PRESET_DRAW_WEIGHT = 300, -} eGPBrush_Presets; - -/* BrushGpencilSettings->flag */ -typedef enum eGPDbrush_Flag { - /* brush use pressure */ - GP_BRUSH_USE_PRESSURE = (1 << 0), - /* brush use pressure for alpha factor */ - GP_BRUSH_USE_STRENGTH_PRESSURE = (1 << 1), - /* brush use pressure for alpha factor */ - GP_BRUSH_USE_JITTER_PRESSURE = (1 << 2), - /* fill hide transparent */ - GP_BRUSH_FILL_HIDE = (1 << 6), - /* show fill help lines */ - GP_BRUSH_FILL_SHOW_HELPLINES = (1 << 7), - /* lazy mouse */ - GP_BRUSH_STABILIZE_MOUSE = (1 << 8), - /* lazy mouse override (internal only) */ - GP_BRUSH_STABILIZE_MOUSE_TEMP = (1 << 9), - /* default eraser brush for quick switch */ - GP_BRUSH_DEFAULT_ERASER = (1 << 10), - /* settings group */ - GP_BRUSH_GROUP_SETTINGS = (1 << 11), - /* Random settings group */ - GP_BRUSH_GROUP_RANDOM = (1 << 12), - /* Keep material assigned to brush */ - GP_BRUSH_MATERIAL_PINNED = (1 << 13), - /* Do not show fill color while drawing (no lasso mode) */ - GP_BRUSH_DISSABLE_LASSO = (1 << 14), - /* Do not erase strokes oLcluded */ - GP_BRUSH_OCCLUDE_ERASER = (1 << 15), - /* Post process trim stroke */ - GP_BRUSH_TRIM_STROKE = (1 << 16), -} eGPDbrush_Flag; - -typedef enum eGPDbrush_Flag2 { - /* Brush use random Hue at stroke level */ - GP_BRUSH_USE_HUE_AT_STROKE = (1 << 0), - /* Brush use random Saturation at stroke level */ - GP_BRUSH_USE_SAT_AT_STROKE = (1 << 1), - /* Brush use random Value at stroke level */ - GP_BRUSH_USE_VAL_AT_STROKE = (1 << 2), - /* Brush use random Pressure at stroke level */ - GP_BRUSH_USE_PRESS_AT_STROKE = (1 << 3), - /* Brush use random Strength at stroke level */ - GP_BRUSH_USE_STRENGTH_AT_STROKE = (1 << 4), - /* Brush use random UV at stroke level */ - GP_BRUSH_USE_UV_AT_STROKE = (1 << 5), - /* Brush use Hue random pressure */ - GP_BRUSH_USE_HUE_RAND_PRESS = (1 << 6), - /* Brush use Saturation random pressure */ - GP_BRUSH_USE_SAT_RAND_PRESS = (1 << 7), - /* Brush use Value random pressure */ - GP_BRUSH_USE_VAL_RAND_PRESS = (1 << 8), - /* Brush use Pressure random pressure */ - GP_BRUSH_USE_PRESSURE_RAND_PRESS = (1 << 9), - /* Brush use Strength random pressure */ - GP_BRUSH_USE_STRENGTH_RAND_PRESS = (1 << 10), - /* Brush use UV random pressure */ - GP_BRUSH_USE_UV_RAND_PRESS = (1 << 11), -} eGPDbrush_Flag2; - -/* BrushGpencilSettings->gp_fill_draw_mode */ -typedef enum eGP_FillDrawModes { - GP_FILL_DMODE_BOTH = 0, - GP_FILL_DMODE_STROKE = 1, - GP_FILL_DMODE_CONTROL = 2, -} eGP_FillDrawModes; - -/* BrushGpencilSettings->fill_layer_mode */ -typedef enum eGP_FillLayerModes { - GP_FILL_GPLMODE_VISIBLE = 0, - GP_FILL_GPLMODE_ACTIVE = 1, - GP_FILL_GPLMODE_ALL_ABOVE = 2, - GP_FILL_GPLMODE_ALL_BELOW = 3, - GP_FILL_GPLMODE_ABOVE = 4, - GP_FILL_GPLMODE_BELOW = 5, -} eGP_FillLayerModes; - -/* BrushGpencilSettings->gp_eraser_mode */ -typedef enum eGP_BrushEraserMode { - GP_BRUSH_ERASER_SOFT = 0, - GP_BRUSH_ERASER_HARD = 1, - GP_BRUSH_ERASER_STROKE = 2, -} eGP_BrushEraserMode; - -/* BrushGpencilSettings->brush_draw_mode */ -typedef enum eGP_BrushMode { - GP_BRUSH_MODE_ACTIVE = 0, - GP_BRUSH_MODE_MATERIAL = 1, - GP_BRUSH_MODE_VERTEXCOLOR = 2, -} eGP_BrushMode; - -/* BrushGpencilSettings default brush icons */ -typedef enum eGP_BrushIcons { - GP_BRUSH_ICON_PENCIL = 1, - GP_BRUSH_ICON_PEN = 2, - GP_BRUSH_ICON_INK = 3, - GP_BRUSH_ICON_INKNOISE = 4, - GP_BRUSH_ICON_BLOCK = 5, - GP_BRUSH_ICON_MARKER = 6, - GP_BRUSH_ICON_FILL = 7, - GP_BRUSH_ICON_ERASE_SOFT = 8, - GP_BRUSH_ICON_ERASE_HARD = 9, - GP_BRUSH_ICON_ERASE_STROKE = 10, - GP_BRUSH_ICON_AIRBRUSH = 11, - GP_BRUSH_ICON_CHISEL = 12, - GP_BRUSH_ICON_TINT = 13, - GP_BRUSH_ICON_VERTEX_DRAW = 14, - GP_BRUSH_ICON_VERTEX_BLUR = 15, - GP_BRUSH_ICON_VERTEX_AVERAGE = 16, - GP_BRUSH_ICON_VERTEX_SMEAR = 17, - GP_BRUSH_ICON_VERTEX_REPLACE = 18, - GP_BRUSH_ICON_GPBRUSH_SMOOTH = 19, - GP_BRUSH_ICON_GPBRUSH_THICKNESS = 20, - GP_BRUSH_ICON_GPBRUSH_STRENGTH = 21, - GP_BRUSH_ICON_GPBRUSH_RANDOMIZE = 22, - GP_BRUSH_ICON_GPBRUSH_GRAB = 23, - GP_BRUSH_ICON_GPBRUSH_PUSH = 24, - GP_BRUSH_ICON_GPBRUSH_TWIST = 25, - GP_BRUSH_ICON_GPBRUSH_PINCH = 26, - GP_BRUSH_ICON_GPBRUSH_CLONE = 27, - GP_BRUSH_ICON_GPBRUSH_WEIGHT = 28, -} eGP_BrushIcons; - -typedef enum eBrushCurvePreset { - BRUSH_CURVE_CUSTOM = 0, - BRUSH_CURVE_SMOOTH = 1, - BRUSH_CURVE_SPHERE = 2, - BRUSH_CURVE_ROOT = 3, - BRUSH_CURVE_SHARP = 4, - BRUSH_CURVE_LIN = 5, - BRUSH_CURVE_POW4 = 6, - BRUSH_CURVE_INVSQUARE = 7, - BRUSH_CURVE_CONSTANT = 8, - BRUSH_CURVE_SMOOTHER = 9, -} eBrushCurvePreset; - -typedef enum eBrushDeformTarget { - BRUSH_DEFORM_TARGET_GEOMETRY = 0, - BRUSH_DEFORM_TARGET_CLOTH_SIM = 1, -} eBrushDeformTarget; - -typedef enum eBrushElasticDeformType { - BRUSH_ELASTIC_DEFORM_GRAB = 0, - BRUSH_ELASTIC_DEFORM_GRAB_BISCALE = 1, - BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE = 2, - BRUSH_ELASTIC_DEFORM_SCALE = 3, - BRUSH_ELASTIC_DEFORM_TWIST = 4, -} eBrushElasticDeformType; - -typedef enum eBrushClothDeformType { - BRUSH_CLOTH_DEFORM_DRAG = 0, - BRUSH_CLOTH_DEFORM_PUSH = 1, - BRUSH_CLOTH_DEFORM_GRAB = 2, - BRUSH_CLOTH_DEFORM_PINCH_POINT = 3, - BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR = 4, - BRUSH_CLOTH_DEFORM_INFLATE = 5, - BRUSH_CLOTH_DEFORM_EXPAND = 6, - BRUSH_CLOTH_DEFORM_SNAKE_HOOK = 7, -} eBrushClothDeformType; - -typedef enum eBrushSmoothDeformType { - BRUSH_SMOOTH_DEFORM_LAPLACIAN = 0, - BRUSH_SMOOTH_DEFORM_SURFACE = 1, -} eBrushSmoothDeformType; - -typedef enum eBrushClothForceFalloffType { - BRUSH_CLOTH_FORCE_FALLOFF_RADIAL = 0, - BRUSH_CLOTH_FORCE_FALLOFF_PLANE = 1, -} eBrushClothForceFalloffType; - -typedef enum eBrushClothSimulationAreaType { - BRUSH_CLOTH_SIMULATION_AREA_LOCAL = 0, - BRUSH_CLOTH_SIMULATION_AREA_GLOBAL = 1, - BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC = 2, -} eBrushClothSimulationAreaType; - -typedef enum eBrushPoseDeformType { - BRUSH_POSE_DEFORM_ROTATE_TWIST = 0, - BRUSH_POSE_DEFORM_SCALE_TRASLATE = 1, - BRUSH_POSE_DEFORM_SQUASH_STRETCH = 2, -} eBrushPoseDeformType; - -typedef enum eBrushPoseOriginType { - BRUSH_POSE_ORIGIN_TOPOLOGY = 0, - BRUSH_POSE_ORIGIN_FACE_SETS = 1, - BRUSH_POSE_ORIGIN_FACE_SETS_FK = 2, -} eBrushPoseOriginType; - -typedef enum eBrushSmearDeformType { - BRUSH_SMEAR_DEFORM_DRAG = 0, - BRUSH_SMEAR_DEFORM_PINCH = 1, - BRUSH_SMEAR_DEFORM_EXPAND = 2, -} eBrushSmearDeformType; - -typedef enum eBrushSlideDeformType { - BRUSH_SLIDE_DEFORM_DRAG = 0, - BRUSH_SLIDE_DEFORM_PINCH = 1, - BRUSH_SLIDE_DEFORM_EXPAND = 2, -} eBrushSlideDeformType; - -typedef enum eBrushBoundaryDeformType { - BRUSH_BOUNDARY_DEFORM_BEND = 0, - BRUSH_BOUNDARY_DEFORM_EXPAND = 1, - BRUSH_BOUNDARY_DEFORM_INFLATE = 2, - BRUSH_BOUNDARY_DEFORM_GRAB = 3, - BRUSH_BOUNDARY_DEFORM_TWIST = 4, - BRUSH_BOUNDARY_DEFORM_SMOOTH = 5, -} eBrushBushBoundaryDeformType; - -typedef enum eBrushBoundaryFalloffType { - BRUSH_BOUNDARY_FALLOFF_CONSTANT = 0, - BRUSH_BOUNDARY_FALLOFF_RADIUS = 1, - BRUSH_BOUNDARY_FALLOFF_LOOP = 2, - BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT = 3, -} eBrushBoundaryFalloffType; - -typedef enum eBrushSnakeHookDeformType { - BRUSH_SNAKE_HOOK_DEFORM_FALLOFF = 0, - BRUSH_SNAKE_HOOK_DEFORM_ELASTIC = 1, -} eBrushSnakeHookDeformType; - -/* Gpencilsettings.Vertex_mode */ -typedef enum eGp_Vertex_Mode { - /* Affect to Stroke only. */ - GPPAINT_MODE_STROKE = 0, - /* Affect to Fill only. */ - GPPAINT_MODE_FILL = 1, - /* Affect to both. */ - GPPAINT_MODE_BOTH = 2, -} eGp_Vertex_Mode; - -/* sculpt_flag */ -typedef enum eGP_Sculpt_Flag { - /* invert the effect of the brush */ - GP_SCULPT_FLAG_INVERT = (1 << 0), - /* smooth brush affects pressure values as well */ - GP_SCULPT_FLAG_SMOOTH_PRESSURE = (1 << 2), - /* temporary invert action */ - GP_SCULPT_FLAG_TMP_INVERT = (1 << 3), -} eGP_Sculpt_Flag; - -/* sculpt_mode_flag */ -typedef enum eGP_Sculpt_Mode_Flag { - /* apply brush to position */ - GP_SCULPT_FLAGMODE_APPLY_POSITION = (1 << 0), - /* apply brush to strength */ - GP_SCULPT_FLAGMODE_APPLY_STRENGTH = (1 << 1), - /* apply brush to thickness */ - GP_SCULPT_FLAGMODE_APPLY_THICKNESS = (1 << 2), - /* apply brush to uv data */ - GP_SCULPT_FLAGMODE_APPLY_UV = (1 << 3), -} eGP_Sculpt_Mode_Flag; - -typedef enum eAutomasking_flag { - BRUSH_AUTOMASKING_TOPOLOGY = (1 << 0), - BRUSH_AUTOMASKING_FACE_SETS = (1 << 1), - BRUSH_AUTOMASKING_BOUNDARY_EDGES = (1 << 2), - BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS = (1 << 3), -} eAutomasking_flag; - -typedef enum ePaintBrush_flag { - BRUSH_PAINT_HARDNESS_PRESSURE = (1 << 0), - BRUSH_PAINT_HARDNESS_PRESSURE_INVERT = (1 << 1), - BRUSH_PAINT_FLOW_PRESSURE = (1 << 2), - BRUSH_PAINT_FLOW_PRESSURE_INVERT = (1 << 3), - BRUSH_PAINT_WET_MIX_PRESSURE = (1 << 4), - BRUSH_PAINT_WET_MIX_PRESSURE_INVERT = (1 << 5), - BRUSH_PAINT_WET_PERSISTENCE_PRESSURE = (1 << 6), - BRUSH_PAINT_WET_PERSISTENCE_PRESSURE_INVERT = (1 << 7), - BRUSH_PAINT_DENSITY_PRESSURE = (1 << 8), - BRUSH_PAINT_DENSITY_PRESSURE_INVERT = (1 << 9), -} ePaintBrush_flag; - typedef struct Brush { ID id; @@ -687,7 +373,7 @@ typedef struct Brush { typedef struct tPaletteColorHSV { float rgb[3]; float value; - float h; + float h; float s; float v; } tPaletteColorHSV; @@ -725,275 +411,6 @@ typedef struct PaintCurve { int add_index; } PaintCurve; -/* Brush.gradient_source */ -typedef enum eBrushGradientSourceStroke { - BRUSH_GRADIENT_PRESSURE = 0, /* gradient from pressure */ - BRUSH_GRADIENT_SPACING_REPEAT = 1, /* gradient from spacing */ - BRUSH_GRADIENT_SPACING_CLAMP = 2, /* gradient from spacing */ -} eBrushGradientSourceStroke; - -typedef enum eBrushGradientSourceFill { - BRUSH_GRADIENT_LINEAR = 0, /* gradient from pressure */ - BRUSH_GRADIENT_RADIAL = 1, /* gradient from spacing */ -} eBrushGradientSourceFill; - -/* Brush.flag */ -typedef enum eBrushFlags { - BRUSH_AIRBRUSH = (1 << 0), - BRUSH_INVERT_TO_SCRAPE_FILL = (1 << 1), - BRUSH_ALPHA_PRESSURE = (1 << 2), - BRUSH_SIZE_PRESSURE = (1 << 3), - BRUSH_JITTER_PRESSURE = (1 << 4), - BRUSH_SPACING_PRESSURE = (1 << 5), - BRUSH_ORIGINAL_PLANE = (1 << 6), - BRUSH_GRAB_ACTIVE_VERTEX = (1 << 7), - BRUSH_ANCHORED = (1 << 8), - BRUSH_DIR_IN = (1 << 9), - BRUSH_SPACE = (1 << 10), - BRUSH_SMOOTH_STROKE = (1 << 11), - BRUSH_PERSISTENT = (1 << 12), - BRUSH_ACCUMULATE = (1 << 13), - BRUSH_LOCK_ALPHA = (1 << 14), - BRUSH_ORIGINAL_NORMAL = (1 << 15), - BRUSH_OFFSET_PRESSURE = (1 << 16), - BRUSH_SCENE_SPACING = (1 << 17), - BRUSH_SPACE_ATTEN = (1 << 18), - BRUSH_ADAPTIVE_SPACE = (1 << 19), - BRUSH_LOCK_SIZE = (1 << 20), - BRUSH_USE_GRADIENT = (1 << 21), - BRUSH_EDGE_TO_EDGE = (1 << 22), - BRUSH_DRAG_DOT = (1 << 23), - BRUSH_INVERSE_SMOOTH_PRESSURE = (1 << 24), - BRUSH_FRONTFACE_FALLOFF = (1 << 25), - BRUSH_PLANE_TRIM = (1 << 26), - BRUSH_FRONTFACE = (1 << 27), - BRUSH_CUSTOM_ICON = (1 << 28), - BRUSH_LINE = (1 << 29), - BRUSH_ABSOLUTE_JITTER = (1 << 30), - BRUSH_CURVE = (1u << 31), -} eBrushFlags; - -/* Brush.sampling_flag */ -typedef enum eBrushSamplingFlags { - BRUSH_PAINT_ANTIALIASING = (1 << 0), -} eBrushSamplingFlags; - -/* Brush.flag2 */ -typedef enum eBrushFlags2 { - BRUSH_MULTIPLANE_SCRAPE_DYNAMIC = (1 << 0), - BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW = (1 << 1), - BRUSH_POSE_IK_ANCHORED = (1 << 2), - BRUSH_USE_CONNECTED_ONLY = (1 << 3), - BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY = (1 << 4), - BRUSH_POSE_USE_LOCK_ROTATION = (1 << 5), - BRUSH_CLOTH_USE_COLLISION = (1 << 6), - BRUSH_AREA_RADIUS_PRESSURE = (1 << 7), - BRUSH_GRAB_SILHOUETTE = (1 << 8), -} eBrushFlags2; - -typedef enum { - BRUSH_MASK_PRESSURE_RAMP = (1 << 1), - BRUSH_MASK_PRESSURE_CUTOFF = (1 << 2), -} BrushMaskPressureFlags; - -/* Brush.overlay_flags */ -typedef enum eOverlayFlags { - BRUSH_OVERLAY_CURSOR = (1), - BRUSH_OVERLAY_PRIMARY = (1 << 1), - BRUSH_OVERLAY_SECONDARY = (1 << 2), - BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE = (1 << 3), - BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE = (1 << 4), - BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE = (1 << 5), -} eOverlayFlags; - -#define BRUSH_OVERLAY_OVERRIDE_MASK \ - (BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE | BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE | \ - BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE) - -/* Brush.sculpt_tool */ -typedef enum eBrushSculptTool { - SCULPT_TOOL_DRAW = 1, - SCULPT_TOOL_SMOOTH = 2, - SCULPT_TOOL_PINCH = 3, - SCULPT_TOOL_INFLATE = 4, - SCULPT_TOOL_GRAB = 5, - SCULPT_TOOL_LAYER = 6, - SCULPT_TOOL_FLATTEN = 7, - SCULPT_TOOL_CLAY = 8, - SCULPT_TOOL_FILL = 9, - SCULPT_TOOL_SCRAPE = 10, - SCULPT_TOOL_NUDGE = 11, - SCULPT_TOOL_THUMB = 12, - SCULPT_TOOL_SNAKE_HOOK = 13, - SCULPT_TOOL_ROTATE = 14, - SCULPT_TOOL_SIMPLIFY = 15, - SCULPT_TOOL_CREASE = 16, - SCULPT_TOOL_BLOB = 17, - SCULPT_TOOL_CLAY_STRIPS = 18, - SCULPT_TOOL_MASK = 19, - SCULPT_TOOL_DRAW_SHARP = 20, - SCULPT_TOOL_ELASTIC_DEFORM = 21, - SCULPT_TOOL_POSE = 22, - SCULPT_TOOL_MULTIPLANE_SCRAPE = 23, - SCULPT_TOOL_SLIDE_RELAX = 24, - SCULPT_TOOL_CLAY_THUMB = 25, - SCULPT_TOOL_CLOTH = 26, - SCULPT_TOOL_DRAW_FACE_SETS = 27, - SCULPT_TOOL_PAINT = 28, - SCULPT_TOOL_SMEAR = 29, - SCULPT_TOOL_BOUNDARY = 30, - SCULPT_TOOL_DISPLACEMENT_ERASER = 31, -} eBrushSculptTool; - -/* Brush.uv_sculpt_tool */ -typedef enum eBrushUVSculptTool { - UV_SCULPT_TOOL_GRAB = 0, - UV_SCULPT_TOOL_RELAX = 1, - UV_SCULPT_TOOL_PINCH = 2, -} eBrushUVSculptTool; - -/** When #BRUSH_ACCUMULATE is used */ -#define SCULPT_TOOL_HAS_ACCUMULATE(t) \ - ELEM(t, \ - SCULPT_TOOL_DRAW, \ - SCULPT_TOOL_DRAW_SHARP, \ - SCULPT_TOOL_SLIDE_RELAX, \ - SCULPT_TOOL_CREASE, \ - SCULPT_TOOL_BLOB, \ - SCULPT_TOOL_INFLATE, \ - SCULPT_TOOL_CLAY, \ - SCULPT_TOOL_CLAY_STRIPS, \ - SCULPT_TOOL_CLAY_THUMB, \ - SCULPT_TOOL_ROTATE, \ - SCULPT_TOOL_SCRAPE, \ - SCULPT_TOOL_FLATTEN) - -#define SCULPT_TOOL_HAS_NORMAL_WEIGHT(t) \ - ELEM(t, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_ELASTIC_DEFORM) - -#define SCULPT_TOOL_HAS_RAKE(t) ELEM(t, SCULPT_TOOL_SNAKE_HOOK) - -#define SCULPT_TOOL_HAS_DYNTOPO(t) \ - (ELEM(t, /* These brushes, as currently coded, cannot support dynamic topology */ \ - SCULPT_TOOL_GRAB, \ - SCULPT_TOOL_ROTATE, \ - SCULPT_TOOL_CLOTH, \ - SCULPT_TOOL_THUMB, \ - SCULPT_TOOL_LAYER, \ - SCULPT_TOOL_DISPLACEMENT_ERASER, \ - SCULPT_TOOL_DRAW_SHARP, \ - SCULPT_TOOL_SLIDE_RELAX, \ - SCULPT_TOOL_ELASTIC_DEFORM, \ - SCULPT_TOOL_BOUNDARY, \ - SCULPT_TOOL_POSE, \ - SCULPT_TOOL_DRAW_FACE_SETS, \ - SCULPT_TOOL_PAINT, \ - SCULPT_TOOL_SMEAR, \ -\ - /* These brushes could handle dynamic topology, \ \ - * but user feedback indicates it's better not to */ \ - SCULPT_TOOL_SMOOTH, \ - SCULPT_TOOL_MASK) == 0) - -#define SCULPT_TOOL_HAS_TOPOLOGY_RAKE(t) \ - (ELEM(t, /* These brushes, as currently coded, cannot support topology rake. */ \ - SCULPT_TOOL_GRAB, \ - SCULPT_TOOL_ROTATE, \ - SCULPT_TOOL_THUMB, \ - SCULPT_TOOL_DRAW_SHARP, \ - SCULPT_TOOL_DISPLACEMENT_ERASER, \ - SCULPT_TOOL_SLIDE_RELAX, \ - SCULPT_TOOL_MASK) == 0) - -/* ImagePaintSettings.tool */ -typedef enum eBrushImagePaintTool { - PAINT_TOOL_DRAW = 0, - PAINT_TOOL_SOFTEN = 1, - PAINT_TOOL_SMEAR = 2, - PAINT_TOOL_CLONE = 3, - PAINT_TOOL_FILL = 4, - PAINT_TOOL_MASK = 5, -} eBrushImagePaintTool; - -typedef enum eBrushVertexPaintTool { - VPAINT_TOOL_DRAW = 0, - VPAINT_TOOL_BLUR = 1, - VPAINT_TOOL_AVERAGE = 2, - VPAINT_TOOL_SMEAR = 3, -} eBrushVertexPaintTool; - -typedef enum eBrushWeightPaintTool { - WPAINT_TOOL_DRAW = 0, - WPAINT_TOOL_BLUR = 1, - WPAINT_TOOL_AVERAGE = 2, - WPAINT_TOOL_SMEAR = 3, -} eBrushWeightPaintTool; - -/* BrushGpencilSettings->brush type */ -typedef enum eBrushGPaintTool { - GPAINT_TOOL_DRAW = 0, - GPAINT_TOOL_FILL = 1, - GPAINT_TOOL_ERASE = 2, - GPAINT_TOOL_TINT = 3, -} eBrushGPaintTool; - -/* BrushGpencilSettings->brush type */ -typedef enum eBrushGPVertexTool { - GPVERTEX_TOOL_DRAW = 0, - GPVERTEX_TOOL_BLUR = 1, - GPVERTEX_TOOL_AVERAGE = 2, - GPVERTEX_TOOL_TINT = 3, - GPVERTEX_TOOL_SMEAR = 4, - GPVERTEX_TOOL_REPLACE = 5, -} eBrushGPVertexTool; - -/* BrushGpencilSettings->brush type */ -typedef enum eBrushGPSculptTool { - GPSCULPT_TOOL_SMOOTH = 0, - GPSCULPT_TOOL_THICKNESS = 1, - GPSCULPT_TOOL_STRENGTH = 2, - GPSCULPT_TOOL_GRAB = 3, - GPSCULPT_TOOL_PUSH = 4, - GPSCULPT_TOOL_TWIST = 5, - GPSCULPT_TOOL_PINCH = 6, - GPSCULPT_TOOL_RANDOMIZE = 7, - GPSCULPT_TOOL_CLONE = 8, -} eBrushGPSculptTool; - -/* BrushGpencilSettings->brush type */ -typedef enum eBrushGPWeightTool { - GPWEIGHT_TOOL_DRAW = 0, -} eBrushGPWeightTool; - -/* direction that the brush displaces along */ -enum { - SCULPT_DISP_DIR_AREA = 0, - SCULPT_DISP_DIR_VIEW = 1, - SCULPT_DISP_DIR_X = 2, - SCULPT_DISP_DIR_Y = 3, - SCULPT_DISP_DIR_Z = 4, -}; - -typedef enum { - BRUSH_MASK_DRAW = 0, - BRUSH_MASK_SMOOTH = 1, -} BrushMaskTool; - -/* blur kernel types, Brush.blur_mode */ -typedef enum eBlurKernelType { - KERNEL_GAUSSIAN = 0, - KERNEL_BOX = 1, -} eBlurKernelType; - -/* Brush.falloff_shape */ -enum { - PAINT_FALLOFF_SHAPE_SPHERE = 0, - PAINT_FALLOFF_SHAPE_TUBE = 1, -}; - -#define MAX_BRUSH_PIXEL_RADIUS 500 -#define GP_MAX_BRUSH_PIXEL_RADIUS 1000 - #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index ae0bb20e529..832d55ea151 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -75,8 +75,7 @@ typedef struct CustomData { * MUST be >= CD_NUMTYPES, but we cant use a define here. * Correct size is ensured in CustomData_update_typemap assert(). */ - int typemap[50]; - char _pad[4]; + int typemap[51]; /** Number of layers, size of layers array. */ int totlayer, maxlayer; /** In editmode, total size of all data layers. */ @@ -156,7 +155,9 @@ typedef enum CustomDataType { CD_PROP_FLOAT3 = 48, CD_PROP_FLOAT2 = 49, - CD_NUMTYPES = 50, + CD_PROP_BOOL = 50, + + CD_NUMTYPES = 51, } CustomDataType; /* Bits for CustomDataMask */ @@ -208,6 +209,7 @@ typedef enum CustomDataType { #define CD_MASK_PROP_COLOR (1ULL << CD_PROP_COLOR) #define CD_MASK_PROP_FLOAT3 (1ULL << CD_PROP_FLOAT3) #define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2) +#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL) /** Multires loop data. */ #define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK) diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index bd5afc457ac..9ac40495887 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -351,6 +351,7 @@ typedef enum eArrayGpencil_Flag { GP_ARRAY_USE_OFFSET = (1 << 7), GP_ARRAY_USE_RELATIVE = (1 << 8), GP_ARRAY_USE_OB_OFFSET = (1 << 9), + GP_ARRAY_UNIFORM_RANDOM_SCALE = (1 << 10), } eArrayGpencil_Flag; typedef struct BuildGpencilModifierData { diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 212f0bfa1c9..949b0bb5bf5 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -32,8 +32,8 @@ extern "C" { #endif struct AnimData; -struct MDeformVert; struct Curve; +struct MDeformVert; #define GP_DEFAULT_PIX_FACTOR 1.0f #define GP_DEFAULT_GRID_LINES 4 diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index 6a91f4857b4..828c6ff2a51 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -40,8 +40,8 @@ typedef enum eViewLayerEEVEEPassType { EEVEE_RENDER_PASS_DIFFUSE_COLOR = (1 << 5), EEVEE_RENDER_PASS_SPECULAR_LIGHT = (1 << 6), EEVEE_RENDER_PASS_SPECULAR_COLOR = (1 << 7), - EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE = (1 << 8), - EEVEE_RENDER_PASS_VOLUME_SCATTER = (1 << 9), + EEVEE_RENDER_PASS_UNUSED_8 = (1 << 8), + EEVEE_RENDER_PASS_VOLUME_LIGHT = (1 << 9), EEVEE_RENDER_PASS_EMIT = (1 << 10), EEVEE_RENDER_PASS_ENVIRONMENT = (1 << 11), EEVEE_RENDER_PASS_SHADOW = (1 << 12), diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 04fbc030ed9..c2337b28e54 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -35,7 +35,6 @@ struct AnimData; struct BVHCache; struct Ipo; struct Key; -struct LinkNode; struct MCol; struct MEdge; struct MFace; @@ -44,7 +43,6 @@ struct MLoopCol; struct MLoopTri; struct MLoopUV; struct MPoly; -struct MPropCol; struct MVert; struct Material; struct Mesh; diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index e122d50cba8..fcb582ef837 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -503,8 +503,8 @@ .random_position = 0.0f, \ .rotation = 0.0f, \ .random_rotation = 0.0f, \ - .particle_offset = 1.0f, \ - .particle_amount = 0.0f, \ + .particle_offset = 0.0f, \ + .particle_amount = 1.0f, \ .index_layer_name = "", \ .value_layer_name = "", \ } diff --git a/source/blender/makesdna/DNA_movieclip_types.h b/source/blender/makesdna/DNA_movieclip_types.h index 2b1fd546450..11cdb48edf0 100644 --- a/source/blender/makesdna/DNA_movieclip_types.h +++ b/source/blender/makesdna/DNA_movieclip_types.h @@ -24,8 +24,8 @@ #pragma once #include "DNA_ID.h" -#include "DNA_color_types.h" /* for color management */ -#include "DNA_tracking_types.h" +#include "DNA_color_types.h" /* for color management */ +#include "DNA_tracking_types.h" /* for #MovieTracking */ #ifdef __cplusplus extern "C" { diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 13f8b11352a..7ad339c66af 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -25,15 +25,15 @@ #include "DNA_ID.h" #include "DNA_listBase.h" -#include "DNA_scene_types.h" -#include "DNA_texture_types.h" -#include "DNA_vec_types.h" +#include "DNA_scene_types.h" /* for #ImageFormatData */ +#include "DNA_vec_types.h" /* for #rctf */ #ifdef __cplusplus extern "C" { #endif struct AnimData; +struct Collection; struct ID; struct Image; struct ListBase; @@ -160,6 +160,7 @@ typedef enum eNodeSocketDatatype { SOCK_OBJECT = 8, SOCK_IMAGE = 9, SOCK_GEOMETRY = 10, + SOCK_COLLECTION = 11, } eNodeSocketDatatype; /* socket shape */ @@ -580,6 +581,10 @@ typedef struct bNodeSocketValueImage { struct Image *value; } bNodeSocketValueImage; +typedef struct bNodeSocketValueCollection { + struct Collection *value; +} bNodeSocketValueCollection; + /* data structs, for node->storage */ enum { CMP_NODE_MASKTYPE_ADD = 0, @@ -1046,10 +1051,20 @@ typedef struct NodeSunBeams { float ray_length; } NodeSunBeams; +typedef struct CryptomatteEntry { + struct CryptomatteEntry *next, *prev; + float encoded_hash; + /** MAX_NAME. */ + char name[64]; + char _pad[4]; +} CryptomatteEntry; + typedef struct NodeCryptomatte { float add[3]; float remove[3]; - char *matte_id; + char *matte_id DNA_DEPRECATED; + /* Contains `CryptomatteEntry`. */ + ListBase entries; int num_inputs; char _pad[4]; } NodeCryptomatte; @@ -1059,6 +1074,20 @@ typedef struct NodeDenoise { char _pad[7]; } NodeDenoise; +typedef struct NodeAttributeMix { + /* e.g. MA_RAMP_BLEND. */ + uint8_t blend_type; + + /* GeometryNodeAttributeInputMode */ + uint8_t input_type_factor; + uint8_t input_type_a; + uint8_t input_type_b; +} NodeAttributeMix; + +typedef struct NodeAttributeColorRamp { + ColorBand color_ramp; +} NodeAttributeColorRamp; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1464,6 +1493,11 @@ typedef enum GeometryNodeUseAttributeFlag { GEO_NODE_USE_ATTRIBUTE_B = (1 << 1), } GeometryNodeUseAttributeFlag; +typedef enum GeometryNodePointInstanceType { + GEO_NODE_POINT_INSTANCE_TYPE_OBJECT = 0, + GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION = 1, +} GeometryNodePointInstanceType; + typedef enum GeometryNodeAttributeInputMode { GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE = 0, GEO_NODE_ATTRIBUTE_INPUT_FLOAT = 1, @@ -1471,6 +1505,11 @@ typedef enum GeometryNodeAttributeInputMode { GEO_NODE_ATTRIBUTE_INPUT_COLOR = 3, } GeometryNodeAttributeInputMode; +typedef enum GeometryNodePointDistributeMethod { + GEO_NODE_POINT_DISTRIBUTE_RANDOM = 0, + GEO_NODE_POINT_DISTRIBUTE_POISSON = 1, +} GeometryNodePointDistributeMethod; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 71f67d8a3b4..8b13db8a012 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -38,9 +38,8 @@ extern "C" { struct AnimData; struct BoundBox; -struct DerivedMesh; struct FluidsimSettings; -struct GpencilBatchCache; +struct GeometrySet; struct Ipo; struct Material; struct Mesh; @@ -51,7 +50,6 @@ struct RigidBodyOb; struct SculptSession; struct SoftBody; struct bGPdata; -struct GeometrySet; /* Vertex Groups - Name Info */ typedef struct bDeformGroup { @@ -150,14 +148,17 @@ typedef struct Object_Runtime { */ struct ID *data_orig; /** - * Object data structure created during object evaluation. - * It has all modifiers applied. + * Object data structure created during object evaluation. It has all modifiers applied. + * The type is determined by the type of the original object. For example, for mesh and curve + * objects, this is a mesh. For a volume object, this is a volume. */ struct ID *data_eval; /** - * Some objects support evaluating to a geometry set instead of a single ID. In those cases the - * evaluated geometry will be stored here instead of in #data_eval. + * Objects can evaluate to a geometry set instead of a single ID. In those cases, the evaluated + * geometry set will be stored here. An ID of the correct type is still stored in #data_eval. + * #geometry_set_eval might reference the ID pointed to by #data_eval as well, but does not own + * the data. */ struct GeometrySet *geometry_set_eval; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 85ec3dfdced..f73f99eb4e7 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -33,15 +33,10 @@ #define USE_SETSCENE_CHECK #include "DNA_ID.h" -#include "DNA_collection_types.h" -#include "DNA_color_types.h" /* color management */ -#include "DNA_curveprofile_types.h" +#include "DNA_color_types.h" /* color management */ #include "DNA_customdata_types.h" /* Scene's runtime cddata masks. */ -#include "DNA_freestyle_types.h" #include "DNA_layer_types.h" #include "DNA_listBase.h" -#include "DNA_material_types.h" -#include "DNA_userdef_types.h" #include "DNA_vec_types.h" #include "DNA_view3d_types.h" @@ -323,8 +318,7 @@ typedef enum eScenePassType { #define RE_PASSNAME_FREESTYLE "Freestyle" #define RE_PASSNAME_BLOOM "BloomCol" -#define RE_PASSNAME_VOLUME_TRANSMITTANCE "VolumeTransmCol" -#define RE_PASSNAME_VOLUME_SCATTER "VolumeScatterCol" +#define RE_PASSNAME_VOLUME_LIGHT "VolumeDir" /* View - MultiView */ typedef struct SceneRenderView { @@ -1345,6 +1339,18 @@ typedef struct MeshStatVis { float sharp_min, sharp_max; } MeshStatVis; +typedef struct SequencerToolSettings { + /* eSeqImageFitMethod */ + int fit_method; +} SequencerToolSettings; + +typedef enum eSeqImageFitMethod { + SEQ_SCALE_TO_FIT, + SEQ_SCALE_TO_FILL, + SEQ_STRETCH_TO_FILL, + SEQ_USE_ORIGINAL_SIZE, +} eSeqImageFitMethod; + /* *************************************************************** */ /* Tool Settings */ @@ -1519,6 +1525,9 @@ typedef struct ToolSettings { * Temporary until there is a proper preset system that stores the profiles or maybe stores * entire bevel configurations. */ struct CurveProfile *custom_bevel_profile_preset; + + struct SequencerToolSettings *sequencer_tool_settings; + } ToolSettings; /* *************************************************************** */ @@ -1783,7 +1792,7 @@ typedef struct Scene { ListBase view_layers; /* Not an actual datablock, but memory owned by scene. */ - Collection *master_collection; + struct Collection *master_collection; struct SceneCollection *collection DNA_DEPRECATED; /** Settings to be override by workspaces. */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index e21f3e1e706..59e5e9df9ee 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -33,9 +33,8 @@ #include "DNA_color_types.h" #include "DNA_defs.h" #include "DNA_listBase.h" -#include "DNA_session_uuid_types.h" -#include "DNA_vec_types.h" -#include "DNA_vfont_types.h" +#include "DNA_session_uuid_types.h" /* for #SessionUUID */ +#include "DNA_vec_types.h" /* for #rctf */ #ifdef __cplusplus extern "C" { @@ -44,6 +43,7 @@ extern "C" { struct Ipo; struct MovieClip; struct Scene; +struct VFont; struct bSound; /* strlens; 256= FILE_MAXFILE, 768= FILE_MAXDIR */ @@ -338,7 +338,7 @@ typedef struct GaussianBlurVars { typedef struct TextVars { char text[512]; - VFont *text_font; + struct VFont *text_font; int text_blf_id; int text_size; float color[4], shadow_color[4], box_color[4]; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 6fd112628a1..4276e8b568e 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -621,6 +621,10 @@ typedef enum eSpaceSeq_Flag { SEQ_SHOW_METADATA = (1 << 10), SEQ_SHOW_MARKERS = (1 << 11), /* show markers region */ SEQ_ZOOM_TO_FIT = (1 << 12), + SEQ_SHOW_STRIP_OVERLAY = (1 << 13), + SEQ_SHOW_STRIP_NAME = (1 << 14), + SEQ_SHOW_STRIP_SOURCE = (1 << 15), + SEQ_SHOW_STRIP_DURATION = (1 << 16), } eSpaceSeq_Flag; /* SpaceSeq.view */ @@ -664,6 +668,24 @@ typedef enum eSpaceSeq_OverlayType { /** \name File Selector * \{ */ +/** + * Information to identify a asset library. May be either one of the predefined types (current + * 'Main', builtin library, project library), or a custom type as defined in the Preferences. + * + * If the type is set to #FILE_ASSET_LIBRARY_CUSTOM, idname must have the name to identify the + * custom library. Otherwise idname is not used. + */ +typedef struct FileSelectAssetLibraryUID { + short type; + char _pad[2]; + /** + * If showing a custom asset library (#FILE_ASSET_LIBRARY_CUSTOM), this is the index of the + * #bUserAssetLibrary within #UserDef.asset_libraries. + * Should be ignored otherwise (but better set to -1 then, for sanity and debugging). + */ + int custom_library_index; +} FileSelectAssetLibraryUID; + /* Config and Input for File Selector */ typedef struct FileSelectParams { /** Title, also used for the text of the execute button. */ @@ -708,6 +730,7 @@ typedef struct FileSelectParams { /** Details toggles (file size, creation date, etc.) */ char details_flags; char _pad2[3]; + /** Filter when (flags & FILE_FILTER) is true. */ int filter; @@ -723,6 +746,32 @@ typedef struct FileSelectParams { /* XXX --- end unused -- */ } FileSelectParams; +/** + * File selection parameters for asset browsing mode, with #FileSelectParams as base. + */ +typedef struct FileAssetSelectParams { + FileSelectParams base_params; + + FileSelectAssetLibraryUID asset_library; +} FileAssetSelectParams; + +/** + * A wrapper to store previous and next folder lists (#FolderList) for a specific browse mode + * (#eFileBrowse_Mode). + */ +typedef struct FileFolderHistory { + struct FileFolderLists *next, *prev; + + /** The browse mode this prev/next folder-lists are created for. */ + char browse_mode; /* eFileBrowse_Mode */ + char _pad[7]; + + /** Holds the list of previous directories to show. */ + ListBase folders_prev; + /** Holds the list of next directories (pushed from previous) to show. */ + ListBase folders_next; +} FileFolderHistory; + /* File Browser */ typedef struct SpaceFile { SpaceLink *next, *prev; @@ -733,20 +782,42 @@ typedef struct SpaceFile { char _pad0[6]; /* End 'SpaceLink' header. */ - char _pad1[4]; + /** Is this a File Browser or an Asset Browser? */ + char browse_mode; /* eFileBrowse_Mode */ + char _pad1[1]; + + short tags; + int scroll_offset; - /** Config and input for file select. */ - struct FileSelectParams *params; + /** Config and input for file select. One for each browse-mode, to keep them independent. */ + FileSelectParams *params; + FileAssetSelectParams *asset_params; - /** Holds the list of files to show. */ + void *_pad2; + + /** + * Holds the list of files to show. + * Currently recreated when browse-mode changes. Could be per browse-mode to avoid refreshes. + */ struct FileList *files; - /** Holds the list of previous directories to show. */ + /** + * Holds the list of previous directories to show. Owned by `folder_histories` below. + */ ListBase *folders_prev; - /** Holds the list of next directories (pushed from previous) to show. */ + /** + * Holds the list of next directories (pushed from previous) to show. Owned by + * `folder_histories` below. + */ ListBase *folders_next; + /** + * This actually owns the prev/next folder-lists above. On browse-mode change, the lists of the + * new mode get assigned to the above. + */ + ListBase folder_histories; /* FileFolderHistory */ + /* operator that is invoking fileselect * op->exec() will be called on the 'Load' button. * if operator provides op->cancel(), then this will be invoked @@ -763,6 +834,30 @@ typedef struct SpaceFile { short systemnr, system_bookmarknr; } SpaceFile; +/* SpaceFile.browse_mode (File Space Browsing Mode) */ +typedef enum eFileBrowse_Mode { + /* Regular Blender File Browser */ + FILE_BROWSE_MODE_FILES = 0, + /* Asset Browser */ + FILE_BROWSE_MODE_ASSETS = 1, +} eFileBrowse_Mode; + +typedef enum eFileAssetLibrary_Type { + /* For the future. Display assets bundled with Blender by default. */ + // FILE_ASSET_LIBRARY_BUNDLED = 0, + /** Display assets from the current session (current "Main"). */ + FILE_ASSET_LIBRARY_LOCAL = 1, + /* For the future. Display assets for the current project. */ + // FILE_ASSET_LIBRARY_PROJECT = 2, + + /** Display assets from custom asset libraries, as defined in the preferences + * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library.idname + * then. + * In RNA, we add the index of the custom library to this to identify it by index. So keep + * this last! */ + FILE_ASSET_LIBRARY_CUSTOM = 100, +} eFileAssetLibrary_Type; + /* FileSelectParams.display */ enum eFileDisplayType { /** Internal (not exposed to users): Keep whatever display type was used during the last File @@ -792,6 +887,13 @@ enum eFileSortType { FILE_SORT_SIZE = 4, }; +/* SpaceFile.tags */ +enum eFileTags { + /** Tag the space as having to update files representing or containing main data. Must be set + * after file read and undo/redo. */ + FILE_TAG_REBUILD_MAIN_FILES = (1 << 0), +}; + /* FileSelectParams.details_flags */ enum eFileDetails { FILE_DETAILS_SIZE = (1 << 0), @@ -810,6 +912,7 @@ enum eFileDetails { typedef enum eFileSelectType { FILE_LOADLIB = 1, FILE_MAIN = 2, + FILE_MAIN_ASSET = 3, FILE_UNIX = 8, FILE_BLENDER = 8, /* don't display relative paths */ @@ -842,6 +945,7 @@ typedef enum eFileSel_Params_Flag { FILE_SORT_INVERT = (1 << 11), FILE_HIDE_TOOL_PROPS = (1 << 12), FILE_CHECK_EXISTING = (1 << 13), + FILE_ASSETS_ONLY = (1 << 14), } eFileSel_Params_Flag; /* sfile->params->rename_flag */ @@ -885,6 +989,7 @@ typedef enum eFileSel_File_Types { FILE_TYPE_USD = (1 << 18), FILE_TYPE_VOLUME = (1 << 19), + FILE_TYPE_ASSET = (1 << 28), /** An FS directory (i.e. S_ISDIR on its path is true). */ FILE_TYPE_DIR = (1 << 30), FILE_TYPE_BLENDERLIB = (1u << 31), @@ -985,9 +1090,16 @@ typedef struct FileDirEntry { /** Optional argument for shortcuts, aliases etc. */ char *redirection_path; - /** TODO: make this a real ID pointer? */ - void *poin; - struct ImBuf *image; + /** When showing local IDs (FILE_MAIN, FILE_MAIN_ASSET), ID this file represents. Note comment + * for FileListInternEntry.local_data, the same applies here! */ + ID *id; + /** If this file represents an asset, its asset data is here. Note that we may show assets of + * external files in which case this is set but not the id above. + * Note comment for FileListInternEntry.local_data, the same applies here! */ + struct AssetMetaData *asset_data; + + /* The icon_id for the preview image. */ + int preview_icon_id; /* Tags are for info only, most of filtering is done in asset engine. */ char **tags; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index f5ac6ca4496..1bd013c7d54 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -569,6 +569,13 @@ enum { USER_MENU_TYPE_PROP = 4, }; +typedef struct bUserAssetLibrary { + struct bUserAssetLibrary *next, *prev; + + char name[64]; /* MAX_NAME */ + char path[1024]; /* FILE_MAX */ +} bUserAssetLibrary; + typedef struct SolidLight { int flag; float smooth; @@ -632,10 +639,12 @@ typedef struct UserDef_Experimental { /* The following options are automatically sanitized (set to 0) * when the release cycle is not alpha. */ char use_new_hair_type; + char use_new_point_cloud_type; char use_sculpt_vertex_colors; char use_switch_object_operator; char use_sculpt_tools_tilt; char use_object_add_tool; + char _pad[7]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; @@ -681,8 +690,7 @@ typedef struct UserDef { short versions; short dbl_click_time; - char _pad0[2]; - char wheellinescroll; + char _pad0[3]; char mini_axis_type; /** #eUserpref_UI_Flag. */ int uiflag; @@ -739,6 +747,8 @@ typedef struct UserDef { struct ListBase autoexec_paths; /** #bUserMenu. */ struct ListBase user_menus; + /** #bUserAssetLibrary */ + struct ListBase asset_libraries; char keyconfigstr[64]; diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index a7ad18d7cf3..f07af2c14a0 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -24,10 +24,8 @@ #pragma once #include "DNA_listBase.h" -#include "DNA_screen_types.h" -#include "DNA_userdef_types.h" -#include "DNA_vec_types.h" -#include "DNA_xr_types.h" +#include "DNA_screen_types.h" /* for #ScrAreaMap */ +#include "DNA_xr_types.h" /* for #XrSessionSettings */ #include "DNA_ID.h" diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index edca887639e..c5c2351c718 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -22,7 +22,7 @@ #pragma once -#include "DNA_scene_types.h" +#include "DNA_ID.h" #ifdef __cplusplus extern "C" { diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt index 2051335dd7e..a7adaa7e258 100644 --- a/source/blender/makesdna/intern/CMakeLists.txt +++ b/source/blender/makesdna/intern/CMakeLists.txt @@ -138,6 +138,7 @@ set(SRC ../../blenlib/intern/hash_mm2a.c ../../blenlib/intern/listbase.c + ../DNA_asset_defaults.h ../DNA_brush_defaults.h ../DNA_cachefile_defaults.h ../DNA_camera_defaults.h diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 1a8bd25215f..3e4d5d87fb0 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -85,6 +85,7 @@ #include "DNA_defaults.h" #include "DNA_armature_types.h" +#include "DNA_asset_types.h" #include "DNA_brush_types.h" #include "DNA_cachefile_types.h" #include "DNA_camera_types.h" @@ -117,6 +118,7 @@ #include "DNA_world_types.h" #include "DNA_armature_defaults.h" +#include "DNA_asset_defaults.h" #include "DNA_brush_defaults.h" #include "DNA_cachefile_defaults.h" #include "DNA_camera_defaults.h" @@ -148,6 +150,9 @@ #define SDNA_DEFAULT_DECL_STRUCT(struct_name) \ static const struct_name DNA_DEFAULT_##struct_name = _DNA_DEFAULT_##struct_name +/* DNA_asset_defaults.h */ +SDNA_DEFAULT_DECL_STRUCT(AssetMetaData); + /* DNA_armature_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(bArmature); @@ -338,7 +343,10 @@ extern const bTheme U_theme_default; /** Keep headers sorted. */ const void *DNA_default_table[SDNA_TYPE_MAX] = { - /* DNA_arnature_defaults.h */ + /* DNA_asset_defaults.h */ + SDNA_DEFAULT_DECL(AssetMetaData), + + /* DNA_armature_defaults.h */ SDNA_DEFAULT_DECL(bArmature), /* DNA_brush_defaults.h */ diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 81a7da7b4d8..54d2bc88d16 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -139,6 +139,7 @@ static const char *includefiles[] = { "DNA_volume_types.h", "DNA_simulation_types.h", "DNA_pointcache_types.h", + "DNA_asset_types.h", /* see comment above before editing! */ @@ -1533,6 +1534,7 @@ int main(int argc, char **argv) #include "DNA_action_types.h" #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_asset_types.h" #include "DNA_boid_types.h" #include "DNA_brush_types.h" #include "DNA_cachefile_types.h" diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index a581edcb04b..aaa948dbff6 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -70,6 +70,8 @@ extern StructRNA RNA_ArrayGpencilModifier; extern StructRNA RNA_ArrayModifier; extern StructRNA RNA_Attribute; extern StructRNA RNA_AttributeGroup; +extern StructRNA RNA_AssetMetaData; +extern StructRNA RNA_AssetTag; extern StructRNA RNA_BackgroundImage; extern StructRNA RNA_BevelModifier; extern StructRNA RNA_BezierSplinePoint; @@ -202,6 +204,7 @@ extern StructRNA RNA_CopyRotationConstraint; extern StructRNA RNA_CopyScaleConstraint; extern StructRNA RNA_CopyTransformsConstraint; extern StructRNA RNA_CorrectiveSmoothModifier; +extern StructRNA RNA_CryptomatteEntry; extern StructRNA RNA_Curve; extern StructRNA RNA_CurveMap; extern StructRNA RNA_CurveMapPoint; @@ -250,7 +253,9 @@ extern StructRNA RNA_FModifierPython; extern StructRNA RNA_FModifierStepped; extern StructRNA RNA_FaceMap; extern StructRNA RNA_FieldSettings; +extern StructRNA RNA_FileAssetSelectParams; extern StructRNA RNA_FileBrowserFSMenuEntry; +extern StructRNA RNA_FileSelectEntry; extern StructRNA RNA_FileSelectParams; extern StructRNA RNA_FloatAttribute; extern StructRNA RNA_FloatAttributeValue; @@ -694,6 +699,7 @@ extern StructRNA RNA_UVProjector; extern StructRNA RNA_UVWarpModifier; extern StructRNA RNA_UnitSettings; extern StructRNA RNA_UnknownType; +extern StructRNA RNA_UserAssetLibrary; extern StructRNA RNA_UserSolidLight; extern StructRNA RNA_VertexcolorGpencilModifier; extern StructRNA RNA_VectorFont; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index a94466e30c2..69bd6142cad 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -56,6 +56,7 @@ extern const EnumPropertyItem rna_enum_mesh_select_mode_items[]; extern const EnumPropertyItem rna_enum_mesh_select_mode_uv_items[]; extern const EnumPropertyItem rna_enum_mesh_delimit_mode_items[]; extern const EnumPropertyItem rna_enum_space_graph_mode_items[]; +extern const EnumPropertyItem rna_enum_space_file_browse_mode_items[]; extern const EnumPropertyItem rna_enum_space_sequencer_view_type_items[]; extern const EnumPropertyItem rna_enum_space_type_items[]; extern const EnumPropertyItem rna_enum_space_image_mode_items[]; diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 9aea5b26a54..3db8909c8a7 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -31,6 +31,7 @@ set(DEFSRC rna_animviz.c rna_armature.c rna_attribute.c + rna_asset.c rna_boid.c rna_brush.c rna_cachefile.c @@ -99,6 +100,7 @@ set(DEFSRC if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_GEOMETRY_NODES) + add_definitions(-DWITH_POINT_CLOUD) add_definitions(-DWITH_HAIR_NODES) list(APPEND DEFSRC rna_hair.c @@ -426,6 +428,7 @@ set(LIB bf_editor_animation bf_editor_armature + bf_editor_asset bf_editor_curve bf_editor_gizmo_library bf_editor_gpencil diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 9eed1fcf085..c2c95c59002 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -4270,6 +4270,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_animviz.c", NULL, RNA_def_animviz}, {"rna_armature.c", "rna_armature_api.c", RNA_def_armature}, {"rna_attribute.c", NULL, RNA_def_attribute}, + {"rna_asset.c", NULL, RNA_def_asset}, {"rna_boid.c", NULL, RNA_def_boid}, {"rna_brush.c", NULL, RNA_def_brush}, {"rna_cachefile.c", NULL, RNA_def_cachefile}, @@ -4308,7 +4309,9 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_packedfile.c", NULL, RNA_def_packedfile}, {"rna_palette.c", NULL, RNA_def_palette}, {"rna_particle.c", NULL, RNA_def_particle}, +#ifdef WITH_POINT_CLOUD {"rna_pointcloud.c", NULL, RNA_def_pointcloud}, +#endif {"rna_pose.c", "rna_pose_api.c", RNA_def_pose}, {"rna_curveprofile.c", NULL, RNA_def_profile}, {"rna_lightprobe.c", NULL, RNA_def_lightprobe}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index d0e0b69a8d5..7c67bfc360b 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -289,9 +289,11 @@ short RNA_type_to_ID_code(const StructRNA *type) if (base_type == &RNA_PaintCurve) { return ID_PC; } +# ifdef WITH_POINT_CLOUD if (base_type == &RNA_PointCloud) { return ID_PT; } +# endif if (base_type == &RNA_LightProbe) { return ID_LP; } @@ -397,7 +399,11 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_PC: return &RNA_PaintCurve; case ID_PT: +# ifdef WITH_POINT_CLOUD return &RNA_PointCloud; +# else + return &RNA_ID; +# endif case ID_LP: return &RNA_LightProbe; case ID_SCE: @@ -1522,6 +1528,11 @@ static void rna_def_ID(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text(prop, "Library", "Library file the data-block is linked from"); + prop = RNA_def_property(srna, "asset_data", PROP_POINTER, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); + RNA_def_property_ui_text(prop, "Asset Data", "Additional data for an asset data-block"); + prop = RNA_def_pointer( srna, "override_library", "IDOverrideLibrary", "Library Override", "Library override data"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 6586cfc7969..4262d5590c8 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -1105,8 +1105,11 @@ bool RNA_struct_bl_idname_ok_or_report(ReportList *reports, const bool failure = false; #endif if (p == NULL || p == identifier || p + len_sep >= identifier + len_id) { - BKE_reportf( - reports, report_level, "'%s' doesn't contain '%s' with prefix & suffix", identifier, sep); + BKE_reportf(reports, + report_level, + "'%s' does not contain '%s' with prefix and suffix", + identifier, + sep); return failure; } @@ -5805,7 +5808,7 @@ ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path) if (r_path) { *r_path = "collection"; } - return (ID *)BKE_collection_master_scene_search(bmain, (Collection *)id); + return (ID *)BKE_collection_master_scene_search(bmain, (struct Collection *)id); default: return NULL; diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c new file mode 100644 index 00000000000..1af53e95cc9 --- /dev/null +++ b/source/blender/makesrna/intern/rna_asset.c @@ -0,0 +1,228 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup RNA + */ + +#include <stdlib.h> + +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "DNA_asset_types.h" +#include "DNA_defs.h" + +#include "rna_internal.h" + +#ifdef RNA_RUNTIME + +# include "BKE_asset.h" +# include "BKE_idprop.h" + +# include "BLI_listbase.h" + +# include "RNA_access.h" + +static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data, + ReportList *reports, + const char *name, + bool skip_if_exists) +{ + AssetTag *tag = NULL; + + if (skip_if_exists) { + struct AssetTagEnsureResult result = BKE_asset_metadata_tag_ensure(asset_data, name); + + if (!result.is_new) { + BKE_reportf( + reports, RPT_WARNING, "Tag '%s' already present for given asset", result.tag->name); + /* Report, but still return valid item. */ + } + tag = result.tag; + } + else { + tag = BKE_asset_metadata_tag_add(asset_data, name); + } + + return tag; +} + +static void rna_AssetMetaData_tag_remove(AssetMetaData *asset_data, + ReportList *reports, + PointerRNA *tag_ptr) +{ + AssetTag *tag = tag_ptr->data; + if (BLI_findindex(&asset_data->tags, tag) == -1) { + BKE_reportf(reports, RPT_ERROR, "Tag '%s' not found in given asset", tag->name); + return; + } + + BKE_asset_metadata_tag_remove(asset_data, tag); + RNA_POINTER_INVALIDATE(tag_ptr); +} + +static IDProperty *rna_AssetMetaData_idprops(PointerRNA *ptr, bool create) +{ + AssetMetaData *asset_data = ptr->data; + + if (create && !asset_data->properties) { + IDPropertyTemplate val = {0}; + asset_data->properties = IDP_New(IDP_GROUP, &val, "RNA_AssetMetaData group"); + } + + return asset_data->properties; +} + +static void rna_AssetMetaData_description_get(PointerRNA *ptr, char *value) +{ + AssetMetaData *asset_data = ptr->data; + + if (asset_data->description) { + strcpy(value, asset_data->description); + } + else { + value[0] = '\0'; + } +} + +static int rna_AssetMetaData_description_length(PointerRNA *ptr) +{ + AssetMetaData *asset_data = ptr->data; + return asset_data->description ? strlen(asset_data->description) : 0; +} + +static void rna_AssetMetaData_description_set(PointerRNA *ptr, const char *value) +{ + AssetMetaData *asset_data = ptr->data; + + if (asset_data->description) { + MEM_freeN(asset_data->description); + } + + if (value[0]) { + asset_data->description = BLI_strdup(value); + } + else { + asset_data->description = NULL; + } +} + +static void rna_AssetMetaData_active_tag_range( + PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + const AssetMetaData *asset_data = ptr->data; + *min = *softmin = 0; + *max = *softmax = MAX2(asset_data->tot_tags - 1, 0); +} + +#else + +static void rna_def_asset_tag(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "AssetTag", NULL); + RNA_def_struct_ui_text(srna, "Asset Tag", "User defined tag (name token)"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, MAX_NAME); + RNA_def_property_ui_text(prop, "Name", "The identifier that makes up this tag"); + RNA_def_struct_name_property(srna, prop); +} + +static void rna_def_asset_tags_api(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "AssetTags"); + srna = RNA_def_struct(brna, "AssetTags", NULL); + RNA_def_struct_sdna(srna, "AssetMetaData"); + RNA_def_struct_ui_text(srna, "Asset Tags", "Collection of custom asset tags"); + + /* Tag collection */ + func = RNA_def_function(srna, "new", "rna_AssetMetaData_tag_new"); + RNA_def_function_ui_description(func, "Add a new tag to this asset"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, + "skip_if_exists", + false, + "Skip if Exists", + "Do not add a new tag if one of the same type already exists"); + /* return type */ + parm = RNA_def_pointer(func, "tag", "AssetTag", "", "New tag"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_AssetMetaData_tag_remove"); + RNA_def_function_ui_description(func, "Remove an existing tag from this asset"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + /* tag to remove */ + parm = RNA_def_pointer(func, "tag", "AssetTag", "", "Removed tag"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); +} + +static void rna_def_asset_data(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "AssetMetaData", NULL); + RNA_def_struct_ui_text(srna, "Asset Data", "Additional data stored for an asset data-block"); + // RNA_def_struct_ui_icon(srna, ICON_ASSET); /* TODO: Icon doesn't exist!. */ + /* The struct has custom properties, but no pointer properties to other IDs! */ + RNA_def_struct_idprops_func(srna, "rna_AssetMetaData_idprops"); + RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */ + + prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_AssetMetaData_description_get", + "rna_AssetMetaData_description_length", + "rna_AssetMetaData_description_set"); + RNA_def_property_ui_text( + prop, "Description", "A description of the asset to be displayed for the user"); + + prop = RNA_def_property(srna, "tags", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "AssetTag"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Tags", + "Custom tags (name tokens) for the asset, used for filtering and " + "general asset management"); + rna_def_asset_tags_api(brna, prop); + + prop = RNA_def_property(srna, "active_tag", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, NULL, NULL, "rna_AssetMetaData_active_tag_range"); + RNA_def_property_ui_text(prop, "Active Tag", "Index of the tag set for editing"); +} + +void RNA_def_asset(BlenderRNA *brna) +{ + RNA_define_animate_sdna(false); + + rna_def_asset_tag(brna); + rna_def_asset_data(brna); + + RNA_define_animate_sdna(true); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 95f6340174a..f98ca47d767 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -44,6 +44,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Float Color", "RGBA color with floating-point precisions"}, {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"}, {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, + {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index b5ce7976fd8..a361feba439 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -22,6 +22,7 @@ #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" +#include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_texture_types.h" @@ -133,6 +134,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""}, {SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""}, {SCULPT_TOOL_DISPLACEMENT_ERASER, "DISPLACEMENT_ERASER", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Eraser", ""}, + {SCULPT_TOOL_DISPLACEMENT_SMEAR, "DISPLACEMENT_SMEAR", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Smear", ""}, {SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_SCULPT_DRAW, "Paint", ""}, {SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "Smear", ""}, {SCULPT_TOOL_DRAW_FACE_SETS, "DRAW_FACE_SETS", ICON_BRUSH_MASK, "Draw Face Sets", ""}, diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index 1810cee5cee..df9f4674900 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -662,7 +662,7 @@ void RNA_def_camera(BlenderRNA *brna) prop = RNA_def_property(srna, "show_safe_center", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOW_SAFE_CENTER); RNA_def_property_ui_text(prop, - "Show Center-cut safe areas", + "Show Center-Cut Safe Areas", "Show safe areas to fit content in a different aspect ratio"); RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL); diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 0b3dc2a3504..4f5828311d8 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -856,7 +856,7 @@ static void rna_def_constraint_headtail_common(StructRNA *srna) prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); + RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head is 0, Tail is 1"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); prop = RNA_def_property(srna, "use_bbone_shape", PROP_BOOLEAN, PROP_NONE); @@ -1051,7 +1051,7 @@ static void rna_def_constraint_python(BlenderRNA *brna) prop = RNA_def_property(srna, "target_count", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "tarnum"); - RNA_def_property_ui_text(prop, "Number of Targets", "Usually only 1-3 are needed"); + RNA_def_property_ui_text(prop, "Number of Targets", "Usually only 1 to 3 are needed"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_dependency_update"); prop = RNA_def_property(srna, "text", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 8734984d4e1..475b4d33936 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -2341,7 +2341,7 @@ static void rna_def_fcurve(BlenderRNA *brna) RNA_def_property_update(prop, NC_ANIMATION, "rna_FCurve_update_data_relations"); /* called 'index' when given as function arg */ - prop = RNA_def_property(srna, "array_index", PROP_INT, PROP_NONE); + prop = RNA_def_property(srna, "array_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_ui_text( prop, "RNA Array Index", "Index to the specific property affected by F-Curve if applicable"); /* XXX need an update callback for this so that animation gets evaluated */ diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index 90f5434bea0..bb8280ede91 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -1552,7 +1552,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_dynamic_array_funcs(prop, "rna_FluidModifier_grid_get_length"); RNA_def_property_float_funcs(prop, "rna_FluidModifier_temperature_grid_get", NULL, NULL); RNA_def_property_ui_text( - prop, "Temperature Grid", "Smoke temperature grid, range 0..1 represents 0..1000K"); + prop, "Temperature Grid", "Smoke temperature grid, range 0 to 1 represents 0 to 1000K"); # endif /* WITH_FLUID */ /* domain object data */ diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 7be9d14b1d1..72e11838fac 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -403,13 +403,13 @@ static char *rna_GPencilLayerMask_path(PointerRNA *ptr) bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); bGPDlayer_Mask *mask = (bGPDlayer_Mask *)ptr->data; - char name_layer[sizeof(gpl->info) * 2]; - char name_mask[sizeof(mask->name) * 2]; + char gpl_info_esc[sizeof(gpl->info) * 2]; + char mask_name_esc[sizeof(mask->name) * 2]; - BLI_str_escape(name_layer, gpl->info, sizeof(name_layer)); - BLI_str_escape(name_mask, mask->name, sizeof(name_mask)); + BLI_str_escape(gpl_info_esc, gpl->info, sizeof(gpl_info_esc)); + BLI_str_escape(mask_name_esc, mask->name, sizeof(mask_name_esc)); - return BLI_sprintfN("layers[\"%s\"].mask_layers[\"%s\"]", name_layer, name_mask); + return BLI_sprintfN("layers[\"%s\"].mask_layers[\"%s\"]", gpl_info_esc, mask_name_esc); } static int rna_GPencil_active_mask_index_get(PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 5f131de6a40..89eb989a442 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -1560,6 +1560,12 @@ static void rna_def_modifier_gpencilarray(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_USE_RELATIVE); RNA_def_property_ui_text(prop, "Shift", "Enable shift"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_uniform_random_scale", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_UNIFORM_RANDOM_SCALE); + RNA_def_property_ui_text( + prop, "Uniform Scale", "Use the same random seed for each scale axis for a uniform scale"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } static void rna_def_modifier_gpencilbuild(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 1c6f83efd65..76c3e17e128 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -152,6 +152,7 @@ void RNA_def_animation(struct BlenderRNA *brna); void RNA_def_animviz(struct BlenderRNA *brna); void RNA_def_armature(struct BlenderRNA *brna); void RNA_def_attribute(struct BlenderRNA *brna); +void RNA_def_asset(struct BlenderRNA *brna); void RNA_def_boid(struct BlenderRNA *brna); void RNA_def_brush(struct BlenderRNA *brna); void RNA_def_cachefile(struct BlenderRNA *brna); @@ -470,7 +471,9 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop); #ifdef WITH_HAIR_NODES void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop); #endif +#ifdef WITH_POINT_CLOUD void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop); +#endif void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop); #ifdef WITH_GEOMETRY_NODES void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop); diff --git a/source/blender/makesrna/intern/rna_light.c b/source/blender/makesrna/intern/rna_light.c index e43079c967f..433d499b4c1 100644 --- a/source/blender/makesrna/intern/rna_light.c +++ b/source/blender/makesrna/intern/rna_light.c @@ -127,7 +127,7 @@ static void rna_def_light(BlenderRNA *brna) prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_light_type_items); - RNA_def_property_ui_text(prop, "Type", "Type of Light"); + RNA_def_property_ui_text(prop, "Type", "Type of light"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_LIGHT); RNA_def_property_update(prop, 0, "rna_Light_draw_update"); diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 0f17f8c44cd..aa22a4307d2 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -128,7 +128,9 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(objects) RNA_MAIN_LISTBASE_FUNCS_DEF(paintcurves) RNA_MAIN_LISTBASE_FUNCS_DEF(palettes) RNA_MAIN_LISTBASE_FUNCS_DEF(particles) +# ifdef WITH_POINT_CLOUD RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds) +# endif RNA_MAIN_LISTBASE_FUNCS_DEF(scenes) RNA_MAIN_LISTBASE_FUNCS_DEF(screens) RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys) @@ -391,12 +393,14 @@ void RNA_def_main(BlenderRNA *brna) # ifdef WITH_HAIR_NODES {"hairs", "Hair", "rna_Main_hairs_begin", "Hairs", "Hair data-blocks", RNA_def_main_hairs}, # endif +# ifdef WITH_POINT_CLOUD {"pointclouds", "PointCloud", "rna_Main_pointclouds_begin", "Point Clouds", "Point cloud data-blocks", RNA_def_main_pointclouds}, +# endif {"volumes", "Volume", "rna_Main_volumes_begin", diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 5fc2cce9bc6..21ff44ed253 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -678,6 +678,7 @@ static Hair *rna_Main_hairs_new(Main *bmain, const char *name) } # endif +# ifdef WITH_POINT_CLOUD static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name) { char safe_name[MAX_ID_NAME - 2]; @@ -687,6 +688,7 @@ static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name) id_us_min(&pointcloud->id); return pointcloud; } +# endif static Volume *rna_Main_volumes_new(Main *bmain, const char *name) { @@ -755,7 +757,9 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP) # ifdef WITH_HAIR_NODES RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA) # endif +# ifdef WITH_POINT_CLOUD RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT) +# endif RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO) # ifdef WITH_GEOMETRY_NODES RNA_MAIN_ID_TAG_FUNCS_DEF(simulations, simulations, ID_SIM) @@ -2206,6 +2210,7 @@ void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop) } # endif +# ifdef WITH_POINT_CLOUD void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -2252,6 +2257,7 @@ void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_boolean(func, "value", 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } +# endif void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop) { diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 086a182e085..94b56e4f4e0 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -683,7 +683,7 @@ void RNA_def_material(BlenderRNA *brna) {MA_SPHERE, "SPHERE", ICON_MATSPHERE, "Sphere", "Sphere"}, {MA_CUBE, "CUBE", ICON_MATCUBE, "Cube", "Cube"}, {MA_HAIR, "HAIR", ICON_HAIR, "Hair", "Hair strands"}, - {MA_SHADERBALL, "SHADERBALL", ICON_MATSHADERBALL, "Shader Ball", "Shader Ball"}, + {MA_SHADERBALL, "SHADERBALL", ICON_MATSHADERBALL, "Shader Ball", "Shader ball"}, {MA_CLOTH, "CLOTH", ICON_MATCLOTH, "Cloth", "Cloth"}, {MA_FLUID, "FLUID", ICON_MATFLUID, "Fluid", "Fluid"}, {0, NULL, 0, NULL, NULL}, diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index d91b6ea19f7..c32cc89aa30 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1603,12 +1603,10 @@ static int rna_MeshSequenceCacheModifier_read_velocity_get(PointerRNA *ptr) # endif } -static bool rna_NodesModifier_node_group_poll(PointerRNA *ptr, PointerRNA value) +static bool rna_NodesModifier_node_group_poll(PointerRNA *UNUSED(ptr), PointerRNA value) { - NodesModifierData *nmd = ptr->data; bNodeTree *ntree = value.data; - UNUSED_VARS(nmd, ntree); - return true; + return ntree->type == NTREE_GEOMETRY; } static void rna_NodesModifier_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -4884,13 +4882,13 @@ static void rna_def_modifier_screw(BlenderRNA *brna) prop = RNA_def_property(srna, "use_stretch_u", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_SCREW_UV_STRETCH_U); RNA_def_property_ui_text( - prop, "Stretch U", "Stretch the U coordinates between 0-1 when UV's are present"); + prop, "Stretch U", "Stretch the U coordinates between 0 and 1 when UV's are present"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "use_stretch_v", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_SCREW_UV_STRETCH_V); RNA_def_property_ui_text( - prop, "Stretch V", "Stretch the V coordinates between 0-1 when UV's are present"); + prop, "Stretch V", "Stretch the V coordinates between 0 and 1 when UV's are present"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); # if 0 @@ -5135,7 +5133,7 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Normalize Weights", - "Normalize the resulting weights (otherwise they are only clamped within [0.0, 1.0] range)"); + "Normalize the resulting weights (otherwise they are only clamped within 0.0 to 1.0 range)"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "map_curve", PROP_POINTER, PROP_NONE); @@ -5300,7 +5298,7 @@ static void rna_def_modifier_weightvgmix(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Normalize Weights", - "Normalize the resulting weights (otherwise they are only clamped within [0.0, 1.0] range)"); + "Normalize the resulting weights (otherwise they are only clamped within 0.0 to 1.0 range)"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); RNA_define_lib_overridable(false); @@ -5424,7 +5422,7 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Normalize Weights", - "Normalize the resulting weights (otherwise they are only clamped within [0.0, 1.0] range)"); + "Normalize the resulting weights (otherwise they are only clamped within 0.0 to 1.0 range)"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "map_curve", PROP_POINTER, PROP_NONE); @@ -5973,7 +5971,7 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna) "FACTOR", 0, "Factor", - "Control playback using a value between [0, 1]"}, + "Control playback using a value between 0 and 1"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index f8a342e7f7e..2642ba82bc0 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -784,7 +784,7 @@ static void rna_def_nlastrip(BlenderRNA *brna) prop = RNA_def_property(srna, "use_animated_time_cyclic", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_TIME_CYCLIC); RNA_def_property_ui_text( - prop, "Cyclic Strip Time", "Cycle the animated time within the action start & end"); + prop, "Cyclic Strip Time", "Cycle the animated time within the action start and end"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index dd02cc214e0..d4ac3d1b084 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -37,6 +37,7 @@ #include "BKE_animsys.h" #include "BKE_attribute.h" +#include "BKE_cryptomatte.h" #include "BKE_image.h" #include "BKE_node.h" #include "BKE_texture.h" @@ -85,6 +86,7 @@ static const EnumPropertyItem node_socket_type_items[] = { {SOCK_OBJECT, "OBJECT", 0, "Object", ""}, {SOCK_IMAGE, "IMAGE", 0, "Image", ""}, {SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""}, + {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -98,6 +100,7 @@ static const EnumPropertyItem node_socket_data_type_items[] = { {SOCK_OBJECT, "OBJECT", 0, "Object", ""}, {SOCK_IMAGE, "IMAGE", 0, "Image", ""}, {SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""}, + {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -272,32 +275,32 @@ const EnumPropertyItem rna_enum_node_float_compare_items[] = { {NODE_FLOAT_COMPARE_LESS_THAN, "LESS_THAN", 0, - "A < B", + "Less Than", "True when the first input is smaller than second input"}, {NODE_FLOAT_COMPARE_LESS_EQUAL, "LESS_EQUAL", 0, - "A <= B", + "Less Than or Equal", "True when the first input is smaller than the second input or equal"}, {NODE_FLOAT_COMPARE_GREATER_THAN, "GREATER_THAN", 0, - "A > B", + "Greater Than", "True when the first input is greater than the second input"}, {NODE_FLOAT_COMPARE_GREATER_EQUAL, "GREATER_EQUAL", 0, - "A >= B", + "Greater Than or Equal", "True when the first input is greater than the second input or equal"}, {NODE_FLOAT_COMPARE_EQUAL, "EQUAL", 0, - "A = B", + "Equal", "True when both inputs are approximately equal"}, {NODE_FLOAT_COMPARE_NOT_EQUAL, "NOT_EQUAL", 0, - "A != B", + "Not Equal", "True when both inputs are not approximately equal"}, {0, NULL, 0, NULL, NULL}, }; @@ -435,6 +438,34 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_b_items[] = { {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem rna_node_geometry_attribute_factor_input_type_items[] = { + {GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""}, + {GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_node_geometry_attribute_input_type_items[] = { + {GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""}, + {GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", ""}, + {GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", ""}, + {GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[] = { + {GEO_NODE_POINT_DISTRIBUTE_RANDOM, + "RANDOM", + 0, + "Random", + "Distribute points randomly on the surface"}, + {GEO_NODE_POINT_DISTRIBUTE_POISSON, + "POISSON", + 0, + "Poisson Disk", + "Project points on the surface evenly with a Poisson disk distribution"}, + {0, NULL, 0, NULL, NULL}, +}; + #endif #ifdef RNA_RUNTIME @@ -1859,7 +1890,7 @@ static const EnumPropertyItem *itemf_function_check( static bool attribute_random_type_supported(const EnumPropertyItem *item) { - return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3); + return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL); } static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf( bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) @@ -1879,6 +1910,30 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_domain_itemf( return itemf_function_check(rna_enum_attribute_domain_items, attribute_random_domain_supported); } +static bool attribute_fill_type_supported(const EnumPropertyItem *item) +{ + return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL); +} +static const EnumPropertyItem *rna_GeometryNodeAttributeFill_type_itemf(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_type_items, attribute_fill_type_supported); +} + +static bool attribute_fill_domain_supported(const EnumPropertyItem *item) +{ + return item->value == ATTR_DOMAIN_POINT; +} +static const EnumPropertyItem *rna_GeometryNodeAttributeFill_domain_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_domain_items, attribute_fill_domain_supported); +} + static bool attribute_math_operation_supported(const EnumPropertyItem *item) { return ELEM(item->value, @@ -3586,33 +3641,26 @@ static void rna_NodeCryptomatte_matte_get(PointerRNA *ptr, char *value) { bNode *node = (bNode *)ptr->data; NodeCryptomatte *nc = node->storage; - - strcpy(value, (nc->matte_id) ? nc->matte_id : ""); + char *matte_id = BKE_cryptomatte_entries_to_matte_id(nc); + strcpy(value, matte_id); + MEM_freeN(matte_id); } static int rna_NodeCryptomatte_matte_length(PointerRNA *ptr) { bNode *node = (bNode *)ptr->data; NodeCryptomatte *nc = node->storage; - - return (nc->matte_id) ? strlen(nc->matte_id) : 0; + char *matte_id = BKE_cryptomatte_entries_to_matte_id(nc); + int result = strlen(matte_id); + MEM_freeN(matte_id); + return result; } static void rna_NodeCryptomatte_matte_set(PointerRNA *ptr, const char *value) { bNode *node = (bNode *)ptr->data; NodeCryptomatte *nc = node->storage; - - if (nc->matte_id) { - MEM_freeN(nc->matte_id); - } - - if (value && value[0]) { - nc->matte_id = BLI_strdup(value); - } - else { - nc->matte_id = NULL; - } + BKE_cryptomatte_matte_id_to_entries(NULL, nc, value); } static void rna_NodeCryptomatte_update_add(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -4268,7 +4316,7 @@ static void def_math(StructRNA *srna) prop = RNA_def_property(srna, "use_clamp", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "custom2", SHD_MATH_CLAMP); - RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0..1 range"); + RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0.0 to 1.0 range"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -4387,7 +4435,7 @@ static void def_mix_rgb(StructRNA *srna) prop = RNA_def_property(srna, "use_clamp", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "custom2", SHD_MIXRGB_CLAMP); - RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0..1 range"); + RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0.0 to 1.0 range"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -5139,12 +5187,12 @@ static void def_sh_tex_pointdensity(StructRNA *srna) "PARTICLE_AGE", 0, "Particle Age", - "Lifetime mapped as 0.0 - 1.0 intensity"}, + "Lifetime mapped as 0.0 to 1.0 intensity"}, {SHD_POINTDENSITY_COLOR_PARTSPEED, "PARTICLE_SPEED", 0, "Particle Speed", - "Particle speed (absolute magnitude of velocity) mapped as 0.0-1.0 intensity"}, + "Particle speed (absolute magnitude of velocity) mapped as 0.0 to 1.0 intensity"}, {SHD_POINTDENSITY_COLOR_PARTVEL, "PARTICLE_VELOCITY", 0, @@ -5899,7 +5947,7 @@ static void def_cmp_map_range(StructRNA *srna) prop = RNA_def_property(srna, "use_clamp", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "custom1", 1); - RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0..1 range"); + RNA_def_property_ui_text(prop, "Clamp", "Clamp result of the node to 0.0 to 1.0 range"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -6799,8 +6847,8 @@ static void def_cmp_defocus(StructRNA *srna) RNA_def_property_range(prop, 0.0f, 128.0f); RNA_def_property_ui_text( prop, - "F-stop", - "Amount of focal blur, 128=infinity=perfect focus, half the value doubles " + "F-Stop", + "Amount of focal blur, 128 (infinity) is perfect focus, half the value doubles " "the blur radius"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -6816,7 +6864,7 @@ static void def_cmp_defocus(StructRNA *srna) RNA_def_property_ui_text( prop, "Threshold", - "CoC radius threshold, prevents background bleed on in-focus midground, 0=off"); + "CoC radius threshold, prevents background bleed on in-focus midground, 0 is disabled"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "use_preview", PROP_BOOLEAN, PROP_NONE); @@ -7602,8 +7650,8 @@ static void def_cmp_bokehblur(StructRNA *srna) RNA_def_property_range(prop, 0.0f, 128.0f); RNA_def_property_ui_text( prop, - "F-stop", - "Amount of focal blur, 128=infinity=perfect focus, half the value doubles " + "F-Stop", + "Amount of focal blur, 128 (infinity) is perfect focus, half the value doubles " "the blur radius"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); # endif @@ -8154,7 +8202,7 @@ static void def_cmp_sunbeams(StructRNA *srna) RNA_def_property_range(prop, -100.0f, 100.0f); RNA_def_property_ui_range(prop, -10.0f, 10.0f, 10, 3); RNA_def_property_ui_text( - prop, "Source", "Source point of rays as a factor of the image width & height"); + prop, "Source", "Source point of rays as a factor of the image width and height"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "ray_length", PROP_FLOAT, PROP_UNSIGNED); @@ -8165,6 +8213,24 @@ static void def_cmp_sunbeams(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_cmp_cryptomatte_entry(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "CryptomatteEntry", NULL); + RNA_def_struct_sdna(srna, "CryptomatteEntry"); + + prop = RNA_def_property(srna, "encoded_hash", PROP_FLOAT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_sdna(prop, NULL, "encoded_hash"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Name", ""); + RNA_def_struct_name_property(srna, prop); +} + static void def_cmp_cryptomatte(StructRNA *srna) { PropertyRNA *prop; @@ -8336,13 +8402,20 @@ static void def_geo_attribute_create_common(StructRNA *srna, RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_random_attribute(StructRNA *srna) +static void def_geo_attribute_randomize(StructRNA *srna) { def_geo_attribute_create_common(srna, "rna_GeometryNodeAttributeRandom_type_itemf", "rna_GeometryNodeAttributeRandom_domain_itemf"); } +static void def_geo_attribute_fill(StructRNA *srna) +{ + def_geo_attribute_create_common(srna, + "rna_GeometryNodeAttributeFill_type_itemf", + "rna_GeometryNodeAttributeFill_domain_itemf"); +} + static void def_geo_attribute_math(StructRNA *srna) { PropertyRNA *prop; @@ -8368,6 +8441,84 @@ static void def_geo_attribute_math(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_point_instance(StructRNA *srna) +{ + static const EnumPropertyItem instance_type_items[] = { + {GEO_NODE_POINT_INSTANCE_TYPE_OBJECT, + "OBJECT", + ICON_NONE, + "Object", + "Instance an individual object on all points"}, + {GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION, + "COLLECTION", + ICON_NONE, + "Collection", + "Instance an entire collection on all points"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + prop = RNA_def_property(srna, "instance_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, instance_type_items); + RNA_def_property_enum_default(prop, GEO_NODE_POINT_INSTANCE_TYPE_OBJECT); + RNA_def_property_ui_text(prop, "Instance Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_attribute_mix(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeAttributeMix", "storage"); + + prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_ramp_blend_items); + RNA_def_property_enum_default(prop, MA_RAMP_BLEND); + RNA_def_property_ui_text(prop, "Blending Mode", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "input_type_factor", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_factor_input_type_items); + RNA_def_property_ui_text(prop, "Input Type Factor", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items); + RNA_def_property_ui_text(prop, "Input Type A", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items); + RNA_def_property_ui_text(prop, "Input Type B", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_point_distribute(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "distribute_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_node_geometry_point_distribute_method_items); + RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_RANDOM); + RNA_def_property_ui_text(prop, "Distribution Method", "Method to use for scattering points"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_attribute_color_ramp(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeAttributeColorRamp", "storage"); + + prop = RNA_def_property(srna, "color_ramp", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ColorRamp"); + RNA_def_property_ui_text(prop, "Color Ramp", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) @@ -8393,6 +8544,8 @@ static void rna_def_compositor_node(BlenderRNA *brna) /* compositor node need_exec flag */ func = RNA_def_function(srna, "tag_need_exec", "rna_CompositorNode_tag_need_exec"); RNA_def_function_ui_description(func, "Tag the node for compositor update"); + + def_cmp_cryptomatte_entry(brna); } static void rna_def_texture_node(BlenderRNA *brna) @@ -9074,6 +9227,41 @@ static void rna_def_node_socket_geometry(BlenderRNA *brna, RNA_def_struct_sdna(srna, "bNodeSocket"); } +static void rna_def_node_socket_collection(BlenderRNA *brna, + const char *identifier, + const char *interface_idname) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Collection Node Socket", "Collection socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "value"); + RNA_def_property_struct_type(prop, "Collection"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + + /* socket interface */ + srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); + RNA_def_struct_ui_text(srna, "Collection Node Socket Interface", "Collection socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "value"); + RNA_def_property_struct_type(prop, "Collection"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); +} + static void rna_def_node_socket_standard_types(BlenderRNA *brna) { /* XXX Workaround: Registered functions are not exposed in python by bpy, @@ -9214,6 +9402,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna) rna_def_node_socket_image(brna, "NodeSocketImage", "NodeSocketInterfaceImage"); rna_def_node_socket_geometry(brna, "NodeSocketGeometry", "NodeSocketInterfaceGeometry"); + + rna_def_node_socket_collection(brna, "NodeSocketCollection", "NodeSocketInterfaceCollection"); } static void rna_def_internal_node(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index ee115b74379..3e8d8e10b37 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -579,7 +579,11 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr) return &RNA_ID; # endif case OB_POINTCLOUD: +# ifdef WITH_POINT_CLOUD return &RNA_PointCloud; +# else + return &RNA_ID; +# endif case OB_VOLUME: return &RNA_Volume; default: @@ -2751,8 +2755,8 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Track Axis", - "Axis that points in 'forward' direction (applies to InstanceFrame when " - "parent 'Follow' is enabled)"); + "Axis that points in the 'forward' direction (applies to Instance Vertices when " + "Align to Vertex Normal is enabled)"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update"); prop = RNA_def_property(srna, "up_axis", PROP_ENUM, PROP_NONE); @@ -2761,8 +2765,8 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Up Axis", - "Axis that points in the upward direction (applies to InstanceFrame when " - "parent 'Follow' is enabled)"); + "Axis that points in the upward direction (applies to Instance Vertices when " + "Align to Vertex Normal is enabled)"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update"); /* proxy */ diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index db4f3754b25..12fd2b78d91 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -21,6 +21,7 @@ #include <stdlib.h> #include "DNA_cloth_types.h" +#include "DNA_dynamicpaint_types.h" #include "DNA_fluid_types.h" #include "DNA_object_force_types.h" #include "DNA_object_types.h" @@ -1486,10 +1487,11 @@ static void rna_def_field(BlenderRNA *brna) prop = RNA_def_property(srna, "texture_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "tex_mode"); RNA_def_property_enum_items(prop, texture_items); - RNA_def_property_ui_text(prop, - "Texture Mode", - "How the texture effect is calculated (RGB & Curl need a RGB texture, " - "else Gradient will be used instead)"); + RNA_def_property_ui_text( + prop, + "Texture Mode", + "How the texture effect is calculated (RGB and Curl need a RGB texture, " + "else Gradient will be used instead)"); RNA_def_property_update(prop, 0, "rna_FieldSettings_update"); prop = RNA_def_property(srna, "z_direction", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index e04ef105071..5fa93ec3f04 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -2948,7 +2948,7 @@ static void rna_def_particle_settings(BlenderRNA *brna) prop, "Adaptive Subframe Threshold", "The relative distance a particle can move before requiring more subframes " - "(target Courant number); 0.01-0.3 is the recommended range"); + "(target Courant number); 0.01 to 0.3 is the recommended range"); RNA_def_property_update(prop, 0, "rna_Particle_reset"); prop = RNA_def_property(srna, "jitter_factor", PROP_FLOAT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index 5f74e8cfc78..aad94f4729f 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -1383,7 +1383,7 @@ static void rna_def_pose_channel(BlenderRNA *brna) "rna_PoseChannel_bone_group_index_set", "rna_PoseChannel_bone_group_index_range"); RNA_def_property_ui_text( - prop, "Bone Group Index", "Bone Group this pose channel belongs to (0=no group)"); + prop, "Bone Group Index", "Bone group this pose channel belongs to (0 means no group)"); RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); @@ -1551,14 +1551,13 @@ static void rna_def_pose_itasc(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Feedback", - "Feedback coefficient for error correction, average response time is 1/feedback " - "(default=20)"); + "Feedback coefficient for error correction, average response time is 1/feedback"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Itasc_update"); prop = RNA_def_property(srna, "velocity_max", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "maxvel"); RNA_def_property_range(prop, 0.0f, 100.0f); - RNA_def_property_ui_text(prop, "Max Velocity", "Maximum joint velocity in rad/s (default=50)"); + RNA_def_property_ui_text(prop, "Max Velocity", "Maximum joint velocity in radians/second"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Itasc_update"); prop = RNA_def_property(srna, "solver", PROP_ENUM, PROP_NONE); @@ -1574,7 +1573,7 @@ static void rna_def_pose_itasc(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Damp", "Maximum damping coefficient when singular value is nearly 0 " - "(higher values=more stability, less reactivity - default=0.5)"); + "(higher values produce results with more stability, less reactivity)"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Itasc_update"); prop = RNA_def_property(srna, "damping_epsilon", PROP_FLOAT, PROP_FACTOR); @@ -1583,7 +1582,7 @@ static void rna_def_pose_itasc(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Epsilon", "Singular value under which damping is progressively applied " - "(higher values=more stability, less reactivity - default=0.1)"); + "(higher values produce results with more stability, less reactivity)"); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Itasc_update"); } diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index 98ae7591062..e2001eacf4c 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -3076,7 +3076,7 @@ static void rna_def_string_property(StructRNA *srna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_string_funcs( prop, "rna_StringProperty_default_get", "rna_StringProperty_default_length", NULL); - RNA_def_property_ui_text(prop, "Default", "string default value"); + RNA_def_property_ui_text(prop, "Default", "String default value"); prop = RNA_def_property(srna, "length_max", PROP_INT, PROP_UNSIGNED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index da03921bca6..7cae88d292b 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1812,7 +1812,9 @@ void rna_ViewLayer_pass_update(Main *bmain, Scene *activescene, PointerRNA *ptr) static char *rna_SceneRenderView_path(PointerRNA *ptr) { SceneRenderView *srv = (SceneRenderView *)ptr->data; - return BLI_sprintfN("render.views[\"%s\"]", srv->name); + char srv_name_esc[sizeof(srv->name) * 2]; + BLI_str_escape(srv_name_esc, srv->name, sizeof(srv_name_esc)); + return BLI_sprintfN("render.views[\"%s\"]", srv_name_esc); } static void rna_Scene_use_nodes_update(bContext *C, PointerRNA *ptr) @@ -2198,6 +2200,11 @@ static char *rna_CurvePaintSettings_path(PointerRNA *UNUSED(ptr)) return BLI_strdup("tool_settings.curve_paint_settings"); } +static char *rna_SequencerToolSettings_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.sequencer_tool_settings"); +} + /* generic function to recalc geometry */ static void rna_EditMesh_update(bContext *C, PointerRNA *UNUSED(ptr)) { @@ -3582,6 +3589,38 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "custom_bevel_profile_preset"); RNA_def_property_struct_type(prop, "CurveProfile"); RNA_def_property_ui_text(prop, "Curve Profile Widget", "Used for defining a profile's path"); + + /* Sequencer tool settings */ + prop = RNA_def_property(srna, "sequencer_tool_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "SequencerToolSettings"); + RNA_def_property_ui_text(prop, "Sequencer Tool Settings", NULL); +} + +static void rna_def_sequencer_tool_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static const EnumPropertyItem scale_fit_methods[] = { + {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"}, + {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"}, + {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image to fill the canvas"}, + {SEQ_USE_ORIGINAL_SIZE, + "ORIGINAL", + 0, + "Use Original Size", + "Keep image at its original size"}, + {0, NULL, 0, NULL, NULL}, + }; + + srna = RNA_def_struct(brna, "SequencerToolSettings", NULL); + RNA_def_struct_path_func(srna, "rna_SequencerToolSettings_path"); + RNA_def_struct_ui_text(srna, "Sequencer Tool Settings", ""); + + prop = RNA_def_property(srna, "fit_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, scale_fit_methods); + RNA_def_property_ui_text(prop, "Fit Method", "Scale fit method"); } static void rna_def_unified_paint_settings(BlenderRNA *brna) @@ -3995,15 +4034,9 @@ static void rna_def_view_layer_eevee(BlenderRNA *brna) srna = RNA_def_struct(brna, "ViewLayerEEVEE", NULL); RNA_def_struct_ui_text(srna, "Eevee Settings", "View layer settings for Eevee"); - prop = RNA_def_property(srna, "use_pass_volume_scatter", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "render_passes", EEVEE_RENDER_PASS_VOLUME_SCATTER); - RNA_def_property_ui_text(prop, "Volume Scatter", "Deliver volume scattering pass"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update"); - - prop = RNA_def_property(srna, "use_pass_volume_transmittance", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna( - prop, NULL, "render_passes", EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE); - RNA_def_property_ui_text(prop, "Volume Transmittance", "Deliver volume transmittance pass"); + prop = RNA_def_property(srna, "use_pass_volume_direct", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "render_passes", EEVEE_RENDER_PASS_VOLUME_LIGHT); + RNA_def_property_ui_text(prop, "Volume Light", "Deliver volume direct light pass"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update"); prop = RNA_def_property(srna, "use_pass_bloom", PROP_BOOLEAN, PROP_NONE); @@ -4319,7 +4352,7 @@ void rna_def_view_layer_common(StructRNA *srna, const bool scene) prop = RNA_def_property(srna, "use_pass_mist", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "passflag", SCE_PASS_MIST); - RNA_def_property_ui_text(prop, "Mist", "Deliver mist factor pass (0.0-1.0)"); + RNA_def_property_ui_text(prop, "Mist", "Deliver mist factor pass (0.0 to 1.0)"); if (scene) { RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update"); } @@ -6366,7 +6399,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_range(prop, 0.0, 1000.0); RNA_def_property_ui_text(prop, "Scale", - "Instead of automatically normalizing to 0..1, " + "Instead of automatically normalizing to the range 0 to 1, " "apply a user scale to the derivative map"); /* stamp */ @@ -7972,6 +8005,7 @@ void RNA_def_scene(BlenderRNA *brna) rna_def_gpencil_interpolate(brna); rna_def_unified_paint_settings(brna); rna_def_curve_paint_settings(brna); + rna_def_sequencer_tool_settings(brna); rna_def_statvis(brna); rna_def_unit_settings(brna); rna_def_scene_image_format_data(brna); diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index ab84dcb0aba..784172b3ac9 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -270,6 +270,8 @@ static void rna_Area_ui_type_update(bContext *C, PointerRNA *ptr) st->space_subtype_set(area, area->butspacetype_subtype); } area->butspacetype_subtype = 0; + + ED_area_tag_refresh(area); } static void rna_View2D_region_to_view(struct View2D *v2d, float x, float y, float result[2]) @@ -586,7 +588,7 @@ static void rna_def_screen(BlenderRNA *brna) prop = RNA_def_property(srna, "show_statusbar", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SCREEN_COLLAPSE_STATUSBAR); - RNA_def_property_ui_text(prop, "Show Status Bar", "Show Status Bar"); + RNA_def_property_ui_text(prop, "Show Status Bar", "Show status bar"); RNA_def_property_update(prop, 0, "rna_Screen_bar_update"); func = RNA_def_function(srna, "statusbar_info", "rna_Screen_statusbar_info_get"); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 9e7fbf2f9a9..eea6fd88ec7 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1201,8 +1201,11 @@ static void rna_SequenceModifier_name_set(PointerRNA *ptr, const char *value) if (adt) { char path[1024]; + char seq_name_esc[(sizeof(seq->name) - 2) * 2]; + BLI_str_escape(seq_name_esc, seq->name + 2, sizeof(seq_name_esc)); + BLI_snprintf( - path, sizeof(path), "sequence_editor.sequences_all[\"%s\"].modifiers", seq->name + 2); + path, sizeof(path), "sequence_editor.sequences_all[\"%s\"].modifiers", seq_name_esc); BKE_animdata_fix_paths_rename(&scene->id, adt, NULL, path, oldname, smd->name, 0, 0, 1); } } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 65ea155ffba..2f821dad811 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -170,6 +170,12 @@ const EnumPropertyItem rna_enum_space_sequencer_view_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_space_file_browse_mode_items[] = { + {FILE_BROWSE_MODE_FILES, "FILES", ICON_FILEBROWSER, "File Browser", ""}, + {FILE_BROWSE_MODE_ASSETS, "ASSETS", ICON_ASSET_MANAGER, "Asset Browser", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #define SACT_ITEM_DOPESHEET \ { \ SACTCONT_DOPESHEET, "DOPESHEET", ICON_ACTION, "Dope Sheet", "Edit all keyframes in scene" \ @@ -412,12 +418,7 @@ static const EnumPropertyItem rna_enum_view3dshading_render_pass_type_items[] = {EEVEE_RENDER_PASS_DIFFUSE_COLOR, "DIFFUSE_COLOR", 0, "Diffuse Color", ""}, {EEVEE_RENDER_PASS_SPECULAR_LIGHT, "SPECULAR_LIGHT", 0, "Specular Light", ""}, {EEVEE_RENDER_PASS_SPECULAR_COLOR, "SPECULAR_COLOR", 0, "Specular Color", ""}, - {EEVEE_RENDER_PASS_VOLUME_TRANSMITTANCE, - "VOLUME_TRANSMITTANCE", - 0, - "Volume Transmittance", - ""}, - {EEVEE_RENDER_PASS_VOLUME_SCATTER, "VOLUME_SCATTER", 0, "Volume Scattering", ""}, + {EEVEE_RENDER_PASS_VOLUME_LIGHT, "VOLUME_LIGHT", 0, "Volume Light", ""}, {0, "", ICON_NONE, "Effects", ""}, {EEVEE_RENDER_PASS_BLOOM, "BLOOM", 0, "Bloom", ""}, @@ -509,6 +510,7 @@ static const EnumPropertyItem rna_enum_curve_display_handle_items[] = { # include "BKE_layer.h" # include "BKE_nla.h" # include "BKE_paint.h" +# include "BKE_preferences.h" # include "BKE_scene.h" # include "BKE_screen.h" # include "BKE_workspace.h" @@ -2467,13 +2469,172 @@ static PointerRNA rna_FileSelectParams_filter_id_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_FileSelectIDFilter, ptr->data); } +static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr) +{ + FileAssetSelectParams *params = ptr->data; + /* Just an extra sanity check to ensure this isn't somehow called for RNA_FileSelectParams. */ + BLI_assert(ptr->type == &RNA_FileAssetSelectParams); + + /* Simple case: Predefined repo, just set the value. */ + if (params->asset_library.type < FILE_ASSET_LIBRARY_CUSTOM) { + return params->asset_library.type; + } + + /* Note that the path isn't checked for validity here. If an invalid library path is used, the + * Asset Browser can give a nice hint on what's wrong. */ + const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( + &U, params->asset_library.custom_library_index); + if (user_library) { + return FILE_ASSET_LIBRARY_CUSTOM + params->asset_library.custom_library_index; + } + + BLI_assert(0); + return FILE_ASSET_LIBRARY_LOCAL; +} + +static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int value) +{ + FileAssetSelectParams *params = ptr->data; + + /* Simple case: Predefined repo, just set the value. */ + if (value < FILE_ASSET_LIBRARY_CUSTOM) { + params->asset_library.type = value; + params->asset_library.custom_library_index = -1; + BLI_assert(ELEM(value, FILE_ASSET_LIBRARY_LOCAL)); + return; + } + + const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( + &U, value - FILE_ASSET_LIBRARY_CUSTOM); + + /* Note that the path isn't checked for validity here. If an invalid library path is used, the + * Asset Browser can give a nice hint on what's wrong. */ + const bool is_valid = (user_library->name[0] && user_library->path[0]); + if (!user_library) { + params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL; + params->asset_library.custom_library_index = -1; + } + else if (user_library && is_valid) { + params->asset_library.custom_library_index = value - FILE_ASSET_LIBRARY_CUSTOM; + params->asset_library.type = FILE_ASSET_LIBRARY_CUSTOM; + } +} + +static const EnumPropertyItem *rna_FileAssetSelectParams_asset_library_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + const EnumPropertyItem predefined_items[] = { + /* For the future. */ + // {FILE_ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"}, + {FILE_ASSET_LIBRARY_LOCAL, + "LOCAL", + ICON_BLENDER, + "Current File", + "Show the assets currently available in this Blender session"}, + {0, NULL, 0, NULL, NULL}, + }; + + EnumPropertyItem *item = NULL; + int totitem = 0; + + /* Add separator if needed. */ + if (!BLI_listbase_is_empty(&U.asset_libraries)) { + const EnumPropertyItem sepr = {0, "", 0, "Custom", NULL}; + RNA_enum_item_add(&item, &totitem, &sepr); + } + + int i = 0; + for (bUserAssetLibrary *user_library = U.asset_libraries.first; user_library; + user_library = user_library->next, i++) { + /* Note that the path itself isn't checked for validity here. If an invalid library path is + * used, the Asset Browser can give a nice hint on what's wrong. */ + const bool is_valid = (user_library->name[0] && user_library->path[0]); + if (!is_valid) { + continue; + } + + /* Use library path as description, it's a nice hint for users. */ + EnumPropertyItem tmp = {FILE_ASSET_LIBRARY_CUSTOM + i, + user_library->name, + ICON_NONE, + user_library->name, + user_library->path}; + RNA_enum_item_add(&item, &totitem, &tmp); + } + + if (totitem) { + const EnumPropertyItem sepr = {0, "", 0, "Built-in", NULL}; + RNA_enum_item_add(&item, &totitem, &sepr); + } + + /* Add predefined items. */ + RNA_enum_items_add(&item, &totitem, predefined_items); + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + return item; +} + +static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value) +{ + FileSelectParams *params = ptr->data; + params->filter_id = value; +} + +static uint64_t rna_FileAssetSelectParams_asset_category_get(PointerRNA *ptr) +{ + FileSelectParams *params = ptr->data; + return params->filter_id; +} + +static void rna_FileBrowser_FileSelectEntry_name_get(PointerRNA *ptr, char *value) +{ + const FileDirEntry *entry = ptr->data; + strcpy(value, entry->name); +} + +static int rna_FileBrowser_FileSelectEntry_name_length(PointerRNA *ptr) +{ + const FileDirEntry *entry = ptr->data; + return (int)strlen(entry->name); +} + +static int rna_FileBrowser_FileSelectEntry_preview_icon_id_get(PointerRNA *ptr) +{ + const FileDirEntry *entry = ptr->data; + return ED_file_icon(entry); +} + +static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr) +{ + const FileDirEntry *entry = ptr->data; + return rna_pointer_inherit_refine(ptr, &RNA_AssetMetaData, entry->asset_data); +} + +static StructRNA *rna_FileBrowser_params_typef(PointerRNA *ptr) +{ + SpaceFile *sfile = ptr->data; + FileSelectParams *params = ED_fileselect_get_active_params(sfile); + + if (params == ED_fileselect_get_file_params(sfile)) { + return &RNA_FileSelectParams; + } + if (params == (void *)ED_fileselect_get_asset_params(sfile)) { + return &RNA_FileAssetSelectParams; + } + + BLI_assert(!"Could not identify file select parameters"); + return NULL; +} + static PointerRNA rna_FileBrowser_params_get(PointerRNA *ptr) { SpaceFile *sfile = ptr->data; FileSelectParams *params = ED_fileselect_get_active_params(sfile); + StructRNA *params_struct = rna_FileBrowser_params_typef(ptr); - if (params) { - return rna_pointer_inherit_refine(ptr, &RNA_FileSelectParams, params); + if (params && params_struct) { + return rna_pointer_inherit_refine(ptr, params_struct, params); } return rna_pointer_inherit_refine(ptr, NULL, NULL); @@ -2789,6 +2950,14 @@ static void rna_FileBrowser_FSMenuRecent_active_range( rna_FileBrowser_FSMenu_active_range(ptr, min, max, softmin, softmax, FS_CATEGORY_RECENT); } +static void rna_SpaceFileBrowser_browse_mode_update(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) +{ + ScrArea *area = rna_area_from_space(ptr); + ED_area_tag_refresh(area); +} + #else static const EnumPropertyItem dt_uv_items[] = { @@ -4281,7 +4450,8 @@ static void rna_def_space_view3d(BlenderRNA *brna) /* Camera Object Data. */ prop = RNA_def_property(srna, "show_gizmo_camera_lens", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_camera", V3D_GIZMO_SHOW_CAMERA_LENS); - RNA_def_property_ui_text(prop, "Show Camera Lens", "Gizmo to adjust camera lens & ortho size"); + RNA_def_property_ui_text( + prop, "Show Camera Lens", "Gizmo to adjust camera focal length or orthographic scale"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); prop = RNA_def_property(srna, "show_gizmo_camera_dof_distance", PROP_BOOLEAN, PROP_NONE); @@ -5006,7 +5176,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna) prop = RNA_def_property(srna, "waveform_display_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, waveform_type_display_items); - RNA_def_property_ui_text(prop, "Waveform Displaying", "How Waveforms are drawn"); + RNA_def_property_ui_text(prop, "Waveform Display", "How Waveforms are drawn"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); prop = RNA_def_property(srna, "use_zoom_to_fit", PROP_BOOLEAN, PROP_NONE); @@ -5059,6 +5229,27 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_FCURVES); RNA_def_property_ui_text(prop, "Show F-Curves", "Display strip opacity/volume curve"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_overlay", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_OVERLAY); + RNA_def_property_ui_text(prop, "Show Overlay", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_name", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_NAME); + RNA_def_property_ui_text(prop, "Show Name", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_source", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_SOURCE); + RNA_def_property_ui_text( + prop, "Show Source", "Display path to source file, or name of source datablock"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_duration", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_DURATION); + RNA_def_property_ui_text(prop, "Show Duration", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } static void rna_def_space_text(BlenderRNA *brna) @@ -5491,7 +5682,7 @@ static void rna_def_space_graph(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SIPO_NORMALIZE); RNA_def_property_ui_text(prop, "Use Normalization", - "Display curves in normalized to -1..1 range, " + "Display curves in normalized range from -1 to 1, " "for easier editing of multiple curves with different ranges"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL); @@ -5798,6 +5989,44 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna) } } +static void rna_def_fileselect_entry(BlenderRNA *brna) +{ + PropertyRNA *prop; + StructRNA *srna = RNA_def_struct(brna, "FileSelectEntry", NULL); + RNA_def_struct_sdna(srna, "FileDirEntry"); + RNA_def_struct_ui_text(srna, "File Select Entry", "A file viewable in the File Browser"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_FileBrowser_FileSelectEntry_name_get", + "rna_FileBrowser_FileSelectEntry_name_length", + NULL); + RNA_def_property_ui_text(prop, "Name", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_int( + srna, + "preview_icon_id", + 0, + INT_MIN, + INT_MAX, + "Icon ID", + "Unique integer identifying the preview of this file as an icon (zero means invalid)", + INT_MIN, + INT_MAX); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_int_funcs( + prop, "rna_FileBrowser_FileSelectEntry_preview_icon_id_get", NULL, NULL); + + prop = RNA_def_property(srna, "asset_data", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "AssetMetaData"); + RNA_def_property_pointer_funcs( + prop, "rna_FileBrowser_FileSelectEntry_asset_data_get", NULL, NULL, NULL); + RNA_def_property_ui_text( + prop, "Asset Data", "Asset data, valid if the file represents an asset"); +} + static void rna_def_fileselect_params(BlenderRNA *brna) { StructRNA *srna; @@ -5970,6 +6199,12 @@ static void rna_def_fileselect_params(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_BLENDER, 0); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + prop = RNA_def_property(srna, "use_filter_asset_only", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", FILE_ASSETS_ONLY); + RNA_def_property_ui_text( + prop, "Only Assets", "Hide .blend files items that are not data-blocks with asset metadata"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + prop = RNA_def_property(srna, "filter_id", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_struct_type(prop, "FileSelectIDFilter"); @@ -6001,6 +6236,75 @@ static void rna_def_fileselect_params(BlenderRNA *brna) RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL); } +static void rna_def_fileselect_asset_params(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* XXX copied from rna_def_fileselect_idfilter. */ + static const EnumPropertyItem asset_category_items[] = { + {FILTER_ID_SCE, "SCENES", ICON_SCENE_DATA, "Scenes", "Show scenes"}, + {FILTER_ID_AC, "ANIMATIONS", ICON_ANIM_DATA, "Animations", "Show animation data"}, + {FILTER_ID_OB | FILTER_ID_GR, + "OBJECTS_AND_COLLECTIONS", + ICON_GROUP, + "Objects & Collections", + "Show objects and collections"}, + {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME + /* XXX avoid warning */ + // | FILTER_ID_HA | FILTER_ID_PT | FILTER_ID_VO + , + "GEOMETRY", + ICON_MESH_DATA, + "Geometry", + "Show meshes, curves, lattice, armatures and metaballs data"}, + {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE, + "SHADING", + ICON_MATERIAL_DATA, + "Shading", + "Show materials, nodetrees, textures and Freestyle's linestyles"}, + {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO, + "IMAGES_AND_SOUNDS", + ICON_IMAGE_DATA, + "Images & Sounds", + "Show images, movie clips, sounds and masks"}, + {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO | FILTER_ID_WS, + "ENVIRONMENTS", + ICON_WORLD_DATA, + "Environment", + "Show worlds, lights, cameras and speakers"}, + {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | + FILTER_ID_VF | FILTER_ID_CF, + "MISC", + ICON_GREASEPENCIL, + "Miscellaneous", + "Show other data types"}, + {0, NULL, 0, NULL, NULL}, + }; + + srna = RNA_def_struct(brna, "FileAssetSelectParams", "FileSelectParams"); + RNA_def_struct_ui_text( + srna, "Asset Select Parameters", "Settings for the file selection in Asset Browser mode"); + + prop = RNA_def_property(srna, "asset_library", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, DummyRNA_NULL_items); + RNA_def_property_enum_funcs(prop, + "rna_FileAssetSelectParams_asset_library_get", + "rna_FileAssetSelectParams_asset_library_set", + "rna_FileAssetSelectParams_asset_library_itemf"); + RNA_def_property_ui_text(prop, "Asset Library", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + + prop = RNA_def_property(srna, "asset_category", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, asset_category_items); + RNA_def_property_enum_funcs(prop, + "rna_FileAssetSelectParams_asset_category_get", + "rna_FileAssetSelectParams_asset_category_set", + NULL); + RNA_def_property_ui_text(prop, "Asset Category", "Determine which kind of assets to display"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL); +} + static void rna_def_filemenu_entry(BlenderRNA *brna) { StructRNA *srna; @@ -6054,9 +6358,18 @@ static void rna_def_space_filebrowser(BlenderRNA *brna) rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_TOOLS) | (1 << RGN_TYPE_UI)); + prop = RNA_def_property(srna, "browse_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_space_file_browse_mode_items); + RNA_def_property_ui_text( + prop, + "Browsing Mode", + "Type of the File Editor view (regular file browsing or asset browsing)"); + RNA_def_property_update(prop, 0, "rna_SpaceFileBrowser_browse_mode_update"); + prop = RNA_def_property(srna, "params", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "FileSelectParams"); - RNA_def_property_pointer_funcs(prop, "rna_FileBrowser_params_get", NULL, NULL, NULL); + RNA_def_property_pointer_funcs( + prop, "rna_FileBrowser_params_get", NULL, "rna_FileBrowser_params_typef", NULL); RNA_def_property_ui_text( prop, "Filebrowser Parameter", "Parameters and Settings for the Filebrowser"); @@ -6804,7 +7117,9 @@ void RNA_def_space(BlenderRNA *brna) rna_def_space_image(brna); rna_def_space_sequencer(brna); rna_def_space_text(brna); + rna_def_fileselect_entry(brna); rna_def_fileselect_params(brna); + rna_def_fileselect_asset_params(brna); rna_def_fileselect_idfilter(brna); rna_def_filemenu_entry(brna); rna_def_space_filebrowser(brna); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index f1f7810bcf0..7a285df235a 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -184,6 +184,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = { # include "BKE_mesh_runtime.h" # include "BKE_paint.h" # include "BKE_pbvh.h" +# include "BKE_preferences.h" # include "BKE_screen.h" # include "DEG_depsgraph.h" @@ -335,6 +336,12 @@ static void rna_userdef_language_update(Main *UNUSED(bmain), USERDEF_TAG_DIRTY; } +static void rna_userdef_asset_library_name_set(PointerRNA *ptr, const char *value) +{ + bUserAssetLibrary *library = (bUserAssetLibrary *)ptr->data; + BKE_preferences_asset_library_name_set(&U, library, value); +} + static void rna_userdef_script_autoexec_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) @@ -4575,7 +4582,7 @@ static void rna_def_userdef_view(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Navigation Controls", - "Show navigation controls in 2D & 3D views which do not have scroll bars"); + "Show navigation controls in 2D and 3D views which do not have scroll bars"); RNA_def_property_update(prop, 0, "rna_userdef_gizmo_update"); /* menus */ @@ -5946,12 +5953,6 @@ static void rna_def_userdef_input(BlenderRNA *brna) prop = RNA_def_property(srna, "invert_zoom_wheel", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_WHEELZOOMDIR); RNA_def_property_ui_text(prop, "Wheel Invert Zoom", "Swap the Mouse Wheel zoom direction"); - - prop = RNA_def_property(srna, "wheel_scroll_lines", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "wheellinescroll"); - RNA_def_property_range(prop, 0, 32); - RNA_def_property_ui_text( - prop, "Wheel Scroll Lines", "Number of lines scrolled at a time with the mouse wheel"); } static void rna_def_userdef_keymap(BlenderRNA *brna) @@ -5974,6 +5975,30 @@ static void rna_def_userdef_keymap(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Key Config", "The name of the active key configuration"); } +static void rna_def_userdef_filepaths_asset_library(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "UserAssetLibrary", NULL); + RNA_def_struct_sdna(srna, "bUserAssetLibrary"); + RNA_def_struct_clear_flag(srna, STRUCT_UNDO); + RNA_def_struct_ui_text( + srna, "Asset Library", "Settings to define a reusable library for Asset Browsers to use"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text( + prop, "Name", "Identifier (not necessarily unique) for the asset library"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_userdef_asset_library_name_set"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, 0, "rna_userdef_update"); + + prop = RNA_def_property(srna, "path", PROP_STRING, PROP_DIRPATH); + RNA_def_property_ui_text( + prop, "Path", "Path to a directory with .blend files to use as an asset library"); + RNA_def_property_update(prop, 0, "rna_userdef_update"); +} + static void rna_def_userdef_filepaths(BlenderRNA *brna) { PropertyRNA *prop; @@ -5984,7 +6009,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) {2, "DJV", 0, "DJV", "Open source frame player: http://djv.sourceforge.net"}, {3, "FRAMECYCLER", 0, "FrameCycler", "Frame player from IRIDAS"}, {4, "RV", 0, "RV", "Frame player from Tweak Software"}, - {5, "MPLAYER", 0, "MPlayer", "Media player for video & png/jpeg/sgi image sequences"}, + {5, "MPLAYER", 0, "MPlayer", "Media player for video and PNG/JPEG/SGI image sequences"}, {50, "CUSTOM", 0, "Custom", "Custom animation player executable path"}, {0, NULL, 0, NULL, NULL}, }; @@ -6067,10 +6092,11 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) prop = RNA_def_property(srna, "script_directory", PROP_STRING, PROP_DIRPATH); RNA_def_property_string_sdna(prop, NULL, "pythondir"); - RNA_def_property_ui_text(prop, - "Python Scripts Directory", - "Alternate script path, matching the default layout with subdirs: " - "startup, add-ons & modules (requires restart)"); + RNA_def_property_ui_text( + prop, + "Python Scripts Directory", + "Alternate script path, matching the default layout with subdirectories: " + "startup, add-ons and modules (requires restart)"); /* TODO, editing should reset sys.path! */ prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH); @@ -6125,7 +6151,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Auto Save Temporary Files", "Automatic saving of temporary files in temp directory, " - "uses process ID (sculpt & edit-mode data won't be saved!)"); + "uses process ID (sculpt and edit mode data won't be saved)"); RNA_def_property_update(prop, 0, "rna_userdef_autosave_update"); prop = RNA_def_property(srna, "auto_save_time", PROP_INT, PROP_NONE); @@ -6145,6 +6171,12 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Save Preview Images", "Enables automatic saving of preview images in the .blend file"); + + rna_def_userdef_filepaths_asset_library(brna); + + prop = RNA_def_property(srna, "asset_libraries", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "UserAssetLibrary"); + RNA_def_property_ui_text(prop, "Asset Libraries", ""); } static void rna_def_userdef_experimental(BlenderRNA *brna) @@ -6165,6 +6197,11 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) "Undo Legacy", "Use legacy undo (slower than the new default one, but may be more stable in some cases)"); + prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1); + RNA_def_property_ui_text( + prop, "New Point Cloud Type", "Enable the new point cloud type in the ui"); + prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1); RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui"); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 26f31412724..3a7addcba94 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -78,6 +78,7 @@ set(SRC intern/MOD_meshsequencecache.c intern/MOD_mirror.c intern/MOD_multires.c + intern/MOD_nodes.cc intern/MOD_none.c intern/MOD_normal_edit.c intern/MOD_ocean.c @@ -88,7 +89,6 @@ set(SRC intern/MOD_shapekey.c intern/MOD_shrinkwrap.c intern/MOD_simpledeform.c - intern/MOD_nodes.cc intern/MOD_skin.c intern/MOD_smooth.c intern/MOD_softbody.c @@ -199,6 +199,7 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_GEOMETRY_NODES) + add_definitions(-DWITH_POINT_CLOUD) add_definitions(-DWITH_HAIR_NODES) endif() diff --git a/source/blender/modifiers/MOD_nodes.h b/source/blender/modifiers/MOD_nodes.h index 9c75e7e3416..907dbe9c5f4 100644 --- a/source/blender/modifiers/MOD_nodes.h +++ b/source/blender/modifiers/MOD_nodes.h @@ -17,8 +17,8 @@ #pragma once struct Main; -struct Object; struct NodesModifierData; +struct Object; #ifdef __cplusplus extern "C" { diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index 38fb19e3233..6769f14f88f 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -295,7 +295,7 @@ ModifierTypeInfo modifierType_Armature = { /* deformMatricesEM */ deformMatricesEM, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index da1754b8ebd..412d6b87d82 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -1018,7 +1018,7 @@ ModifierTypeInfo modifierType_Array = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index 04ddac338e5..a94411d897e 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -447,7 +447,7 @@ ModifierTypeInfo modifierType_Bevel = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, /* requiredDataMask */ requiredDataMask, diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c index 0513d3af13a..e290fd9dab7 100644 --- a/source/blender/modifiers/intern/MOD_boolean.c +++ b/source/blender/modifiers/intern/MOD_boolean.c @@ -819,7 +819,7 @@ ModifierTypeInfo modifierType_Boolean = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index 96ed0a5d069..0b1c661baed 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -346,7 +346,7 @@ ModifierTypeInfo modifierType_Build = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 06bd9ada0fb..f905a38ae12 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -590,7 +590,7 @@ ModifierTypeInfo modifierType_Cast = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 8f876213cd6..a25d65347c5 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -33,6 +33,7 @@ #include "DNA_defaults.h" #include "DNA_key_types.h" #include "DNA_mesh_types.h" +#include "DNA_object_force_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -116,7 +117,7 @@ static void deformVerts(ModifierData *md, mesh_src = (Mesh *)BKE_id_copy_ex(NULL, (ID *)mesh, NULL, LIB_ID_COPY_LOCALIZE); } - /* TODO(sergey): For now it actually duplicates logic from DerivedMesh.c + /* TODO(sergey): For now it actually duplicates logic from DerivedMesh.cc * and needs some more generic solution. But starting experimenting with * this so close to the release is not that nice.. * @@ -309,7 +310,7 @@ ModifierTypeInfo modifierType_Cloth = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index faba08f9613..a80bac7c6de 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -23,6 +23,7 @@ #include "BLI_utildefines.h" +#include "BLI_kdopbvh.h" #include "BLI_math.h" #include "BLT_translation.h" @@ -30,6 +31,7 @@ #include "DNA_defaults.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_force_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" @@ -309,7 +311,7 @@ ModifierTypeInfo modifierType_Collision = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index 5884ec0aa17..15c4e8af6ce 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -851,7 +851,7 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index bed53cb0f51..d5d53edfd54 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -235,7 +235,7 @@ ModifierTypeInfo modifierType_Curve = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index d4c941d144d..8b299a82f95 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -494,7 +494,7 @@ ModifierTypeInfo modifierType_DataTransfer = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 10ed4f8d80b..b28b8fd39fc 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -299,7 +299,7 @@ ModifierTypeInfo modifierType_Decimate = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index d432559fcfa..abe78943508 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -506,7 +506,7 @@ ModifierTypeInfo modifierType_Displace = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index b69179f464d..7f57183ff13 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -220,7 +220,7 @@ ModifierTypeInfo modifierType_DynamicPaint = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index 30c38623f68..e02befd7efa 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -186,7 +186,7 @@ ModifierTypeInfo modifierType_EdgeSplit = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index d5e065ad321..c12019a325e 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -1254,7 +1254,7 @@ ModifierTypeInfo modifierType_Explode = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index 6dc4fe79f88..8a8d6f2305f 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -238,7 +238,7 @@ ModifierTypeInfo modifierType_Fluid = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index e0c362171f2..2fa05a319d5 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -573,7 +573,7 @@ ModifierTypeInfo modifierType_Hook = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index a484b4d8147..bda0f9ba5a4 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -888,7 +888,7 @@ ModifierTypeInfo modifierType_LaplacianDeform = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index d51f95bd18d..fc527304e76 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -634,7 +634,7 @@ ModifierTypeInfo modifierType_LaplacianSmooth = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ init_data, diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index 5aadc171394..e3c42e39dda 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -192,7 +192,7 @@ ModifierTypeInfo modifierType_Lattice = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 92ee5a84df9..191d39d9fce 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -462,7 +462,7 @@ ModifierTypeInfo modifierType_Mask = { /* deformMatricesEM */ nullptr, /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, - /* modifyPointCloud */ nullptr, + /* modifyGeometrySet */ nullptr, /* modifyVolume */ nullptr, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index e137a59e10f..cc007651733 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -295,7 +295,7 @@ ModifierTypeInfo modifierType_MeshToVolume = { /* deformMatricesEM */ nullptr, /* modifyMesh */ nullptr, /* modifyHair */ nullptr, - /* modifyPointCloud */ nullptr, + /* modifyGeometrySet */ nullptr, /* modifyVolume */ modifyVolume, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index b808d738fe8..6d2e0f242d7 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -388,7 +388,7 @@ ModifierTypeInfo modifierType_MeshCache = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index 0e530312238..4bd306e7679 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -646,7 +646,7 @@ ModifierTypeInfo modifierType_MeshDeform = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 73106b2e816..2c01857adb1 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -270,7 +270,7 @@ ModifierTypeInfo modifierType_MeshSequenceCache = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index 9346f601981..7f34c6581ad 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -234,7 +234,7 @@ ModifierTypeInfo modifierType_Mirror = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c index 9f99e036601..1182c8db093 100644 --- a/source/blender/modifiers/intern/MOD_multires.c +++ b/source/blender/modifiers/intern/MOD_multires.c @@ -519,7 +519,7 @@ ModifierTypeInfo modifierType_Multires = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 80c69398d15..6bb747fa715 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -33,6 +33,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "DNA_collection_types.h" #include "DNA_defaults.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -104,6 +105,12 @@ static void addIdsUsedBySocket(const ListBase *sockets, Set<ID *> &ids) ids.add(&object->id); } } + else if (socket->type == SOCK_COLLECTION) { + Collection *collection = ((bNodeSocketValueCollection *)socket->default_value)->value; + if (collection != nullptr) { + ids.add(&collection->id); + } + } } } @@ -399,6 +406,11 @@ class GeometryNodesEvaluator { blender::bke::PersistentObjectHandle object_handle = handle_map_.lookup(object); new (buffer) blender::bke::PersistentObjectHandle(object_handle); } + else if (bsocket->type == SOCK_COLLECTION) { + Collection *collection = ((bNodeSocketValueCollection *)bsocket->default_value)->value; + blender::bke::PersistentCollectionHandle collection_handle = handle_map_.lookup(collection); + new (buffer) blender::bke::PersistentCollectionHandle(collection_handle); + } else { blender::nodes::socket_cpp_value_get(*bsocket, buffer); } @@ -439,6 +451,8 @@ static IDProperty *socket_add_property(IDProperty *settings_prop_group, IDProperty *prop = property_type.create_prop(socket, new_prop_name); IDP_AddToGroup(settings_prop_group, prop); + prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY; + /* Make the group in the ui container group to hold the property's UI settings. */ IDProperty *prop_ui_group; { @@ -848,6 +862,9 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md) else if (socket->type == SOCK_GEOMETRY) { BKE_modifier_set_error(ob, md, "Node group can only have one geometry input"); } + else if (socket->type == SOCK_COLLECTION) { + BKE_modifier_set_error(ob, md, "Collection socket can not be exposed in the modifier"); + } else { BKE_modifier_set_error(ob, md, "Missing property for input socket \"%s\"", socket->name); } @@ -934,9 +951,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return new_mesh; } -static void modifyPointCloud(ModifierData *md, - const ModifierEvalContext *ctx, - GeometrySet *geometry_set) +static void modifyGeometrySet(ModifierData *md, + const ModifierEvalContext *ctx, + GeometrySet *geometry_set) { modifyGeometry(md, ctx, *geometry_set); } @@ -960,8 +977,12 @@ static void draw_property_for_socket(uiLayout *layout, /* IDProperties can be removed with python, so there could be a situation where * there isn't a property for a socket or it doesn't have the correct type. */ if (property != nullptr && property_type->is_correct_type(*property)) { - char rna_path[128]; - BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket.identifier); + + char socket_id_esc[sizeof(socket.identifier) * 2]; + BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + + char rna_path[sizeof(socket_id_esc) + 4]; + BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc); uiItemR(layout, settings_ptr, rna_path, 0, socket.name, ICON_NONE); } } @@ -1043,6 +1064,15 @@ static void freeData(ModifierData *md) } } +static void requiredDataMask(Object *UNUSED(ob), + ModifierData *UNUSED(md), + CustomData_MeshMasks *r_cddata_masks) +{ + /* We don't know what the node tree will need. If there are vertex groups, it is likely that the + * node tree wants to access them. */ + r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT; +} + ModifierTypeInfo modifierType_Nodes = { /* name */ "GeometryNodes", /* structName */ "NodesModifierData", @@ -1050,9 +1080,9 @@ ModifierTypeInfo modifierType_Nodes = { /* srna */ &RNA_NodesModifier, /* type */ eModifierTypeType_Constructive, /* flags */ - static_cast<ModifierTypeFlag>(eModifierTypeFlag_AcceptsMesh | - eModifierTypeFlag_SupportsEditmode | - eModifierTypeFlag_EnableInEditmode), + static_cast<ModifierTypeFlag>( + eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode | + eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping), /* icon */ ICON_NODETREE, /* copyData */ copyData, @@ -1063,11 +1093,11 @@ ModifierTypeInfo modifierType_Nodes = { /* deformMatricesEM */ nullptr, /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, - /* modifyPointCloud */ modifyPointCloud, + /* modifyGeometrySet */ modifyGeometrySet, /* modifyVolume */ nullptr, /* initData */ initData, - /* requiredDataMask */ nullptr, + /* requiredDataMask */ requiredDataMask, /* freeData */ freeData, /* isDisabled */ isDisabled, /* updateDepsgraph */ updateDepsgraph, diff --git a/source/blender/modifiers/intern/MOD_none.c b/source/blender/modifiers/intern/MOD_none.c index 02307622d06..4daa527577b 100644 --- a/source/blender/modifiers/intern/MOD_none.c +++ b/source/blender/modifiers/intern/MOD_none.c @@ -59,7 +59,7 @@ ModifierTypeInfo modifierType_None = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ NULL, diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index 0ec564d2e2d..ec10b18665e 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -804,7 +804,7 @@ ModifierTypeInfo modifierType_NormalEdit = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 5aef497c0c4..9c940745920 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -736,7 +736,7 @@ ModifierTypeInfo modifierType_Ocean = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index f660874a5ea..e7f1fa9943e 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -678,7 +678,7 @@ ModifierTypeInfo modifierType_ParticleInstance = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index 86480a17083..e49f16b994c 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -331,7 +331,7 @@ ModifierTypeInfo modifierType_ParticleSystem = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index f8fc4ad658e..175435fcd44 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -300,7 +300,7 @@ ModifierTypeInfo modifierType_Remesh = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 6521a0859e5..ba370b401f3 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -701,8 +701,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /*printf("flip direction %i\n", ed_loop_flip);*/ - /* switch the flip option if set - * note: flip is now done at face level so copying vgroup slizes is easier */ + /* Switch the flip option if set + * NOTE: flip is now done at face level so copying group slices is easier. */ #if 0 if (do_flip) { ed_loop_flip = !ed_loop_flip; @@ -1259,7 +1259,7 @@ ModifierTypeInfo modifierType_Screw = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c index bb267386dfb..81a0ee72496 100644 --- a/source/blender/modifiers/intern/MOD_shapekey.c +++ b/source/blender/modifiers/intern/MOD_shapekey.c @@ -27,6 +27,7 @@ #include "DNA_key_types.h" #include "DNA_mesh_types.h" +#include "DNA_object_types.h" #include "BKE_key.h" #include "BKE_particle.h" @@ -139,7 +140,7 @@ ModifierTypeInfo modifierType_ShapeKey = { /* deformMatricesEM */ deformMatricesEM, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ NULL, diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index dddabb12f61..93626309727 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -292,7 +292,7 @@ ModifierTypeInfo modifierType_Shrinkwrap = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index ec89176f97e..e41e70864dc 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -552,7 +552,7 @@ ModifierTypeInfo modifierType_SimpleDeform = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 6936f5a53f8..5e412185cea 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -2043,7 +2043,7 @@ ModifierTypeInfo modifierType_Skin = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c index e73a59f1ae8..618ccc78279 100644 --- a/source/blender/modifiers/intern/MOD_smooth.c +++ b/source/blender/modifiers/intern/MOD_smooth.c @@ -32,6 +32,7 @@ #include "DNA_defaults.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_types.h" #include "DNA_screen_types.h" #include "BKE_context.h" @@ -286,7 +287,7 @@ ModifierTypeInfo modifierType_Smooth = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index 4bdb3ba60b1..9a657c44fca 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -119,7 +119,7 @@ ModifierTypeInfo modifierType_Softbody = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ NULL, diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 8886d3718c9..8e519a72df1 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -29,6 +29,7 @@ #include "DNA_defaults.h" #include "DNA_mesh_types.h" +#include "DNA_object_types.h" #include "DNA_screen_types.h" #include "BKE_context.h" @@ -274,7 +275,7 @@ ModifierTypeInfo modifierType_Solidify = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index c8b357b34c8..99069919120 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -26,6 +26,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_types.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index 8acf07f9181..f62980ec4fd 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -24,6 +24,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_types.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index 1aa015682dd..d089894a6e9 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -507,7 +507,7 @@ ModifierTypeInfo modifierType_Subsurf = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index 72f19efe3a4..7416a4baf38 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -240,7 +240,7 @@ ModifierTypeInfo modifierType_Surface = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index 5407397e3bf..e00c5ba7f04 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1546,7 +1546,7 @@ ModifierTypeInfo modifierType_SurfaceDeform = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index 1930a38b825..04d24ac0883 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -177,7 +177,7 @@ ModifierTypeInfo modifierType_Triangulate = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index fa5243c548f..55dbfdf478f 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -307,10 +307,16 @@ static void modifier_panel_header(const bContext *C, Panel *panel) /* Modifier Icon. */ sub = uiLayoutRow(layout, true); + uiLayoutSetEmboss(sub, UI_EMBOSS_NONE); if (mti->isDisabled && mti->isDisabled(scene, md, 0)) { uiLayoutSetRedAlert(sub, true); } - uiItemL(sub, "", RNA_struct_ui_icon(ptr->type)); + uiItemStringO(sub, + "", + RNA_struct_ui_icon(ptr->type), + "OBJECT_OT_modifier_set_active", + "modifier", + md->name); row = uiLayoutRow(layout, true); diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index bfbc27abb88..8122f4617c1 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -370,7 +370,7 @@ ModifierTypeInfo modifierType_UVProject = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c index b326494815e..77b79167c2f 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ b/source/blender/modifiers/intern/MOD_uvwarp.c @@ -341,7 +341,7 @@ ModifierTypeInfo modifierType_UVWarp = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index 90e570f55fb..745e089b8ff 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -328,7 +328,7 @@ ModifierTypeInfo modifierType_VolumeDisplace = { /* deformMatricesEM */ nullptr, /* modifyMesh */ nullptr, /* modifyHair */ nullptr, - /* modifyPointCloud */ nullptr, + /* modifyGeometrySet */ nullptr, /* modifyVolume */ modifyVolume, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index ec53914f115..941bc8409f7 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -335,7 +335,7 @@ ModifierTypeInfo modifierType_VolumeToMesh = { /* deformMatricesEM */ nullptr, /* modifyMesh */ modifyMesh, /* modifyHair */ nullptr, - /* modifyPointCloud */ nullptr, + /* modifyGeometrySet */ nullptr, /* modifyVolume */ nullptr, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 53e41484606..9d3d5b0658c 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -538,7 +538,7 @@ ModifierTypeInfo modifierType_Warp = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index 45f06a7778c..863656b85a5 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -491,7 +491,7 @@ ModifierTypeInfo modifierType_Wave = { /* deformMatricesEM */ NULL, /* modifyMesh */ NULL, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index bd15d909834..40265e37db9 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -763,7 +763,7 @@ ModifierTypeInfo modifierType_WeightedNormal = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index df554f6bc4e..915adccc745 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -427,7 +427,7 @@ ModifierTypeInfo modifierType_WeightVGEdit = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 9be36fe6846..52cee7ce7e5 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -513,7 +513,7 @@ ModifierTypeInfo modifierType_WeightVGMix = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 7232ffd3d9d..aac29cabf0f 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -767,7 +767,7 @@ ModifierTypeInfo modifierType_WeightVGProximity = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c index e34dcf48c19..fd1254fc948 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.c @@ -2052,7 +2052,7 @@ ModifierTypeInfo modifierType_Weld = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index 206c514826c..3d8e74d2cf5 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -195,7 +195,7 @@ ModifierTypeInfo modifierType_Wireframe = { /* deformMatricesEM */ NULL, /* modifyMesh */ modifyMesh, /* modifyHair */ NULL, - /* modifyPointCloud */ NULL, + /* modifyGeometrySet */ NULL, /* modifyVolume */ NULL, /* initData */ initData, diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index a367f40dca7..bc0e7972bcb 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -22,9 +22,9 @@ set(INC . composite function + geometry intern shader - geometry texture ../blenkernel ../blenlib @@ -138,16 +138,20 @@ set(SRC function/nodes/node_fn_switch.cc function/node_function_util.cc + geometry/nodes/node_geo_attribute_color_ramp.cc + geometry/nodes/node_geo_attribute_fill.cc geometry/nodes/node_geo_attribute_math.cc - geometry/nodes/node_geo_common.cc + geometry/nodes/node_geo_attribute_randomize.cc geometry/nodes/node_geo_boolean.cc + geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_join_geometry.cc + geometry/nodes/node_geo_attribute_mix.cc geometry/nodes/node_geo_object_info.cc - geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_point_distribute.cc + geometry/nodes/node_geo_point_distribute_poisson_disk.cc geometry/nodes/node_geo_point_instance.cc - geometry/nodes/node_geo_random_attribute.cc + geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc geometry/node_geometry_exec.cc @@ -295,12 +299,12 @@ set(SRC NOD_composite.h NOD_derived_node_tree.hh NOD_function.h + NOD_geometry.h + NOD_math_functions.hh NOD_node_tree_dependencies.hh NOD_node_tree_multi_function.hh NOD_node_tree_ref.hh NOD_shader.h - NOD_geometry.h - NOD_math_functions.hh NOD_socket.h NOD_static_types.h NOD_texture.h @@ -311,9 +315,9 @@ set(SRC ) set(LIB + bf_bmesh bf_functions bf_intern_sky - bf_bmesh ) if(WITH_PYTHON) diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 0532547375f..f1cd55ce048 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -26,6 +26,7 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); +void register_node_type_geo_attribute_fill(void); void register_node_type_geo_boolean(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_transform(void); @@ -34,9 +35,11 @@ void register_node_type_geo_triangulate(void); void register_node_type_geo_point_distribute(void); void register_node_type_geo_point_instance(void); void register_node_type_geo_object_info(void); -void register_node_type_geo_random_attribute(void); +void register_node_type_geo_attribute_randomize(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_join_geometry(void); +void register_node_type_geo_attribute_mix(void); +void register_node_type_geo_attribute_color_ramp(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index a7df4bc3e1b..cac04e18fc7 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -26,12 +26,16 @@ namespace blender::nodes { +using bke::BooleanReadAttribute; +using bke::BooleanWriteAttribute; using bke::Color4fReadAttribute; using bke::Color4fWriteAttribute; using bke::Float3ReadAttribute; using bke::Float3WriteAttribute; using bke::FloatReadAttribute; using bke::FloatWriteAttribute; +using bke::Int32ReadAttribute; +using bke::Int32WriteAttribute; using bke::PersistentDataHandleMap; using bke::PersistentObjectHandle; using bke::ReadAttribute; @@ -168,10 +172,21 @@ class GeoNodeExecParams { return this->get_input_attribute(name, component, domain, type, &default_value); } + /** + * Get the type of an input property or the associated constant socket types with the + * same names. Fall back to the default value if no attribute exists with the name. + */ + CustomDataType get_input_attribute_data_type(const StringRef name, + const GeometryComponent &component, + const CustomDataType default_type) const; + private: /* Utilities for detecting common errors at when using this class. */ void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const; void check_set_output(StringRef identifier, const CPPType &value_type) const; + + /* Find the active socket socket with the input name (not the identifier). */ + const bNodeSocket *find_available_socket(const StringRef name) const; }; } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 09e0908140c..662952abb59 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -271,12 +271,15 @@ DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Spl DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") -DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, 0, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "") -DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, 0, "POINT_INSTANCE", PointInstance, "Point Instance", "") +DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "") +DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "") -DefNode(GeometryNode, GEO_NODE_RANDOM_ATTRIBUTE, def_geo_random_attribute, "RANDOM_ATTRIBUTE", RandomAttribute, "Random Attribute", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c index c8d2d993e75..f5308fe2671 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.c @@ -27,172 +27,36 @@ #include "BLI_utildefines.h" #include "node_composite_util.h" -/* this is taken from the cryptomatte specification 1.0 */ - -BLI_INLINE float hash_to_float(uint32_t hash) +static CryptomatteEntry *cryptomatte_find(NodeCryptomatte *n, float encoded_hash) { - uint32_t mantissa = hash & ((1 << 23) - 1); - uint32_t exponent = (hash >> 23) & ((1 << 8) - 1); - exponent = MAX2(exponent, (uint32_t)1); - exponent = MIN2(exponent, (uint32_t)254); - exponent = exponent << 23; - uint32_t sign = (hash >> 31); - sign = sign << 31; - uint32_t float_bits = sign | exponent | mantissa; - float f; - /* Bit casting relies on equal size for both types. */ - BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), "float and uint32_t are not the same size") - memcpy(&f, &float_bits, sizeof(float)); - return f; + LISTBASE_FOREACH (CryptomatteEntry *, entry, &n->entries) { + if (entry->encoded_hash == encoded_hash) { + return entry; + } + } + return NULL; } static void cryptomatte_add(NodeCryptomatte *n, float f) { - /* Turn the number into a string. */ - char number[32]; - BLI_snprintf(number, sizeof(number), "<%.9g>", f); - - /* Search if we already have the number. */ - if (n->matte_id && strlen(n->matte_id) != 0) { - size_t start = 0; - const size_t end = strlen(n->matte_id); - size_t token_len = 0; - while (start < end) { - /* Ignore leading whitespace. */ - while (start < end && n->matte_id[start] == ' ') { - start++; - } - - /* Find the next separator. */ - char *token_end = strchr(n->matte_id + start, ','); - if (ELEM(token_end, NULL, n->matte_id + start)) { - token_end = n->matte_id + end; - } - /* Be aware that token_len still contains any trailing white space. */ - token_len = token_end - (n->matte_id + start); - - /* If this has a leading bracket, - * assume a raw floating point number and look for the closing bracket. */ - if (n->matte_id[start] == '<') { - if (strncmp(n->matte_id + start, number, strlen(number)) == 0) { - /* This number is already there, so continue. */ - return; - } - } - else { - /* Remove trailing white space */ - size_t name_len = token_len; - while (n->matte_id[start + name_len] == ' ' && name_len > 0) { - name_len--; - } - /* Calculate the hash of the token and compare. */ - uint32_t hash = BLI_hash_mm3((const unsigned char *)(n->matte_id + start), name_len, 0); - if (f == hash_to_float(hash)) { - return; - } - } - start += token_len + 1; - } - } - - DynStr *new_matte = BLI_dynstr_new(); - if (!new_matte) { + /* Check if entry already exist. */ + if (cryptomatte_find(n, f) != NULL) { return; } - - if (n->matte_id) { - BLI_dynstr_append(new_matte, n->matte_id); - MEM_freeN(n->matte_id); - } - - if (BLI_dynstr_get_len(new_matte) > 0) { - BLI_dynstr_append(new_matte, ","); - } - BLI_dynstr_append(new_matte, number); - n->matte_id = BLI_dynstr_get_cstring(new_matte); - BLI_dynstr_free(new_matte); + CryptomatteEntry *entry = MEM_callocN(sizeof(CryptomatteEntry), __func__); + entry->encoded_hash = f; + BLI_addtail(&n->entries, entry); } static void cryptomatte_remove(NodeCryptomatte *n, float f) { - if (n->matte_id == NULL || strlen(n->matte_id) == 0) { - /* Empty string, nothing to remove. */ + CryptomatteEntry *entry = cryptomatte_find(n, f); + if (entry == NULL) { return; } - /* This will be the new string without the removed key. */ - DynStr *new_matte = BLI_dynstr_new(); - if (!new_matte) { - return; - } - - /* Turn the number into a string. */ - static char number[32]; - BLI_snprintf(number, sizeof(number), "<%.9g>", f); - - /* Search if we already have the number. */ - size_t start = 0; - const size_t end = strlen(n->matte_id); - size_t token_len = 0; - bool is_first = true; - while (start < end) { - bool skip = false; - /* Ignore leading whitespace or commas. */ - while (start < end && ((n->matte_id[start] == ' ') || (n->matte_id[start] == ','))) { - start++; - } - - /* Find the next separator. */ - char *token_end = strchr(n->matte_id + start + 1, ','); - if (ELEM(token_end, NULL, n->matte_id + start)) { - token_end = n->matte_id + end; - } - /* Be aware that token_len still contains any trailing white space. */ - token_len = token_end - (n->matte_id + start); - - if (token_len == 1) { - skip = true; - } - /* If this has a leading bracket, - * assume a raw floating point number and look for the closing bracket. */ - else if (n->matte_id[start] == '<') { - if (strncmp(n->matte_id + start, number, strlen(number)) == 0) { - /* This number is already there, so skip it. */ - skip = true; - } - } - else { - /* Remove trailing white space */ - size_t name_len = token_len; - while (n->matte_id[start + name_len] == ' ' && name_len > 0) { - name_len--; - } - /* Calculate the hash of the token and compare. */ - uint32_t hash = BLI_hash_mm3((const unsigned char *)(n->matte_id + start), name_len, 0); - if (f == hash_to_float(hash)) { - skip = true; - } - } - if (!skip) { - if (is_first) { - is_first = false; - } - else { - BLI_dynstr_append(new_matte, ", "); - } - BLI_dynstr_nappend(new_matte, n->matte_id + start, token_len); - } - start += token_len + 1; - } - - if (n->matte_id) { - MEM_freeN(n->matte_id); - n->matte_id = NULL; - } - if (BLI_dynstr_get_len(new_matte) > 0) { - n->matte_id = BLI_dynstr_get_cstring(new_matte); - } - BLI_dynstr_free(new_matte); + BLI_remlink(&n->entries, entry); + MEM_freeN(entry); } static bNodeSocketTemplate outputs[] = { @@ -265,10 +129,7 @@ static void node_free_cryptomatte(bNode *node) NodeCryptomatte *nc = node->storage; if (nc) { - if (nc->matte_id) { - MEM_freeN(nc->matte_id); - } - + BLI_freelistN(&nc->entries); MEM_freeN(nc); } } @@ -280,10 +141,7 @@ static void node_copy_cryptomatte(bNodeTree *UNUSED(dest_ntree), NodeCryptomatte *src_nc = src_node->storage; NodeCryptomatte *dest_nc = MEM_dupallocN(src_nc); - if (src_nc->matte_id) { - dest_nc->matte_id = MEM_dupallocN(src_nc->matte_id); - } - + BLI_duplicatelist(&dest_nc->entries, &src_nc->entries); dest_node->storage = dest_nc; } diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index ec389961615..c97463cdc22 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -42,4 +42,10 @@ namespace blender::nodes { void update_attribute_input_socket_availabilities(bNode &node, const StringRef name, const GeometryNodeAttributeInputMode mode); -} + +void poisson_disk_point_elimination(Vector<float3> const *input_points, + Vector<float3> *output_points, + float maximum_distance, + float3 boundbox); + +} // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc new file mode 100644 index 00000000000..3f7023ba88f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -0,0 +1,107 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "BKE_colorband.h" + +static bNodeSocketTemplate geo_node_attribute_color_ramp_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Attribute")}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_color_ramp_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component) +{ + const bNode &bnode = params.node(); + NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)bnode.storage; + + const std::string result_name = params.get_input<std::string>("Result"); + /* Once we support more domains at the user level, we have to decide how the result domain is + * choosen. */ + const AttributeDomain result_domain = ATTR_DOMAIN_POINT; + const CustomDataType result_type = CD_PROP_COLOR; + + WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write( + result_name, result_domain, result_type); + if (!attribute_result) { + return; + } + + Color4fWriteAttribute attribute_out = std::move(attribute_result); + + const std::string input_name = params.get_input<std::string>("Attribute"); + FloatReadAttribute attribute_in = component.attribute_get_for_read<float>( + input_name, result_domain, 0.0f); + + Span<float> data_in = attribute_in.get_span(); + MutableSpan<Color4f> data_out = attribute_out.get_span(); + + ColorBand *color_ramp = &node_storage->color_ramp; + for (const int i : data_in.index_range()) { + BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]); + } + + attribute_out.apply_span(); +} + +static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + if (geometry_set.has<MeshComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); + } + if (geometry_set.has<PointCloudComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN( + sizeof(NodeAttributeColorRamp), __func__); + BKE_colorband_init(&node_storage->color_ramp, true); + node->storage = node_storage; +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_color_ramp() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_COLOR_RAMP, "Attribute Color Ramp", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_color_ramp_in, geo_node_attribute_color_ramp_out); + node_type_storage( + &ntype, "NodeAttributeColorRamp", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, blender::nodes::geo_node_attribute_color_ramp_init); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_color_ramp_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc new file mode 100644 index 00000000000..5cdbd18ecc8 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -0,0 +1,141 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "BLI_rand.hh" + +#include "DNA_mesh_types.h" +#include "DNA_pointcloud_types.h" + +static bNodeSocketTemplate geo_node_attribute_fill_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Attribute")}, + {SOCK_VECTOR, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, + {SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, + {SOCK_BOOLEAN, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_fill_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_fill_init(bNodeTree *UNUSED(tree), bNode *node) +{ + node->custom1 = CD_PROP_FLOAT; +} + +static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2); + bNodeSocket *socket_value_float = socket_value_vector->next; + bNodeSocket *socket_value_color4f = socket_value_float->next; + bNodeSocket *socket_value_boolean = socket_value_color4f->next; + + const CustomDataType data_type = static_cast<CustomDataType>(node->custom1); + + nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL); +} + +namespace blender::nodes { + +static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms) +{ + const bNode &node = params.node(); + const CustomDataType data_type = static_cast<CustomDataType>(node.custom1); + const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2); + const std::string attribute_name = params.get_input<std::string>("Attribute"); + if (attribute_name.empty()) { + return; + } + + WriteAttributePtr attribute = component.attribute_try_ensure_for_write( + attribute_name, domain, data_type); + if (!attribute) { + return; + } + + switch (data_type) { + case CD_PROP_FLOAT: { + FloatWriteAttribute float_attribute = std::move(attribute); + const float value = params.get_input<float>("Value_001"); + MutableSpan<float> attribute_span = float_attribute.get_span(); + attribute_span.fill(value); + float_attribute.apply_span(); + break; + } + case CD_PROP_FLOAT3: { + Float3WriteAttribute float3_attribute = std::move(attribute); + const float3 value = params.get_input<float3>("Value"); + MutableSpan<float3> attribute_span = float3_attribute.get_span(); + attribute_span.fill(value); + float3_attribute.apply_span(); + break; + } + case CD_PROP_COLOR: { + Color4fWriteAttribute color4f_attribute = std::move(attribute); + const Color4f value = params.get_input<Color4f>("Value_002"); + MutableSpan<Color4f> attribute_span = color4f_attribute.get_span(); + attribute_span.fill(value); + color4f_attribute.apply_span(); + break; + } + case CD_PROP_BOOL: { + BooleanWriteAttribute boolean_attribute = std::move(attribute); + const bool value = params.get_input<bool>("Value_003"); + MutableSpan<bool> attribute_span = boolean_attribute.get_span(); + attribute_span.fill(value); + boolean_attribute.apply_span(); + break; + } + default: + break; + } +} + +static void geo_node_attribute_fill_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + if (geometry_set.has<MeshComponent>()) { + fill_attribute(geometry_set.get_component_for_write<MeshComponent>(), params); + } + if (geometry_set.has<PointCloudComponent>()) { + fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); + } + + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_fill() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates(&ntype, geo_node_attribute_fill_in, geo_node_attribute_fill_out); + node_type_init(&ntype, geo_node_attribute_fill_init); + node_type_update(&ntype, geo_node_attribute_fill_update); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_fill_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc new file mode 100644 index 00000000000..2f2558a2d53 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -0,0 +1,221 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_material.h" + +#include "DNA_material_types.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_attribute_mix_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Factor")}, + {SOCK_FLOAT, N_("Factor"), 0.5, 0.0, 0.0, 0.0, 0.0, 1.0, PROP_FACTOR}, + {SOCK_STRING, N_("A")}, + {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("A"), 0.5, 0.5, 0.5, 1.0}, + {SOCK_STRING, N_("B")}, + {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("B"), 0.5, 0.5, 0.5, 1.0}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mix_attribute_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void do_mix_operation_float(const int blend_mode, + const FloatReadAttribute &factors, + const FloatReadAttribute &inputs_a, + const FloatReadAttribute &inputs_b, + FloatWriteAttribute &results) +{ + const int size = results.size(); + for (const int i : IndexRange(size)) { + const float factor = factors[i]; + float3 a{inputs_a[i]}; + const float3 b{inputs_b[i]}; + ramp_blend(blend_mode, a, factor, b); + const float result = a.length(); + results.set(i, result); + } +} + +static void do_mix_operation_float3(const int blend_mode, + const FloatReadAttribute &factors, + const Float3ReadAttribute &inputs_a, + const Float3ReadAttribute &inputs_b, + Float3WriteAttribute &results) +{ + const int size = results.size(); + for (const int i : IndexRange(size)) { + const float factor = factors[i]; + float3 a = inputs_a[i]; + const float3 b = inputs_b[i]; + ramp_blend(blend_mode, a, factor, b); + results.set(i, a); + } +} + +static void do_mix_operation_color4f(const int blend_mode, + const FloatReadAttribute &factors, + const Color4fReadAttribute &inputs_a, + const Color4fReadAttribute &inputs_b, + Color4fWriteAttribute &results) +{ + const int size = results.size(); + for (const int i : IndexRange(size)) { + const float factor = factors[i]; + Color4f a = inputs_a[i]; + const Color4f b = inputs_b[i]; + ramp_blend(blend_mode, a, factor, b); + results.set(i, a); + } +} + +static void do_mix_operation(const CustomDataType result_type, + int blend_mode, + const FloatReadAttribute &attribute_factor, + ReadAttributePtr attribute_a, + ReadAttributePtr attribute_b, + WriteAttributePtr attribute_result) +{ + if (result_type == CD_PROP_FLOAT) { + FloatReadAttribute attribute_a_float = std::move(attribute_a); + FloatReadAttribute attribute_b_float = std::move(attribute_b); + FloatWriteAttribute attribute_result_float = std::move(attribute_result); + do_mix_operation_float(blend_mode, + attribute_factor, + attribute_a_float, + attribute_b_float, + attribute_result_float); + } + else if (result_type == CD_PROP_FLOAT3) { + Float3ReadAttribute attribute_a_float3 = std::move(attribute_a); + Float3ReadAttribute attribute_b_float3 = std::move(attribute_b); + Float3WriteAttribute attribute_result_float3 = std::move(attribute_result); + do_mix_operation_float3(blend_mode, + attribute_factor, + attribute_a_float3, + attribute_b_float3, + attribute_result_float3); + } + else if (result_type == CD_PROP_COLOR) { + Color4fReadAttribute attribute_a_color4f = std::move(attribute_a); + Color4fReadAttribute attribute_b_color4f = std::move(attribute_b); + Color4fWriteAttribute attribute_result_color4f = std::move(attribute_result); + do_mix_operation_color4f(blend_mode, + attribute_factor, + attribute_a_color4f, + attribute_b_color4f, + attribute_result_color4f); + } +} + +static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms) +{ + const bNode &node = params.node(); + const NodeAttributeMix *node_storage = (const NodeAttributeMix *)node.storage; + + CustomDataType result_type = CD_PROP_COLOR; + AttributeDomain result_domain = ATTR_DOMAIN_POINT; + + /* Use type and domain from the result attribute, if it exists already. */ + const std::string result_name = params.get_input<std::string>("Result"); + const ReadAttributePtr result_attribute_read = component.attribute_try_get_for_read(result_name); + if (result_attribute_read) { + result_type = result_attribute_read->custom_data_type(); + result_domain = result_attribute_read->domain(); + } + + WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write( + result_name, result_domain, result_type); + if (!attribute_result) { + return; + } + + FloatReadAttribute attribute_factor = params.get_input_attribute<float>( + "Factor", component, result_domain, 0.5f); + ReadAttributePtr attribute_a = params.get_input_attribute( + "A", component, result_domain, result_type, nullptr); + ReadAttributePtr attribute_b = params.get_input_attribute( + "B", component, result_domain, result_type, nullptr); + + do_mix_operation(result_type, + node_storage->blend_type, + attribute_factor, + std::move(attribute_a), + std::move(attribute_b), + std::move(attribute_result)); +} + +static void geo_node_attribute_mix_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + if (geometry_set.has<MeshComponent>()) { + attribute_mix_calc(geometry_set.get_component_for_write<MeshComponent>(), params); + } + if (geometry_set.has<PointCloudComponent>()) { + attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); + } + + params.set_output("Geometry", geometry_set); +} + +static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix), + "attribute mix node"); + data->blend_type = MA_RAMP_BLEND; + data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + node->storage = data; +} + +static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage; + update_attribute_input_socket_availabilities( + *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); + update_attribute_input_socket_availabilities( + *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); + update_attribute_input_socket_availabilities( + *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_mix() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates(&ntype, geo_node_attribute_mix_in, geo_node_mix_attribute_out); + node_type_init(&ntype, blender::nodes::geo_node_attribute_mix_init); + node_type_update(&ntype, blender::nodes::geo_node_attribute_mix_update); + node_type_storage( + &ntype, "NodeAttributeMix", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_mix_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index 5cacb96412c..2c3acfc9735 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -16,12 +16,13 @@ #include "node_geometry_util.hh" +#include "BLI_hash.h" #include "BLI_rand.hh" #include "DNA_mesh_types.h" #include "DNA_pointcloud_types.h" -static bNodeSocketTemplate geo_node_random_attribute_in[] = { +static bNodeSocketTemplate geo_node_attribute_randomize_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, {SOCK_STRING, N_("Attribute")}, {SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, @@ -32,17 +33,17 @@ static bNodeSocketTemplate geo_node_random_attribute_in[] = { {-1, ""}, }; -static bNodeSocketTemplate geo_node_random_attribute_out[] = { +static bNodeSocketTemplate geo_node_attribute_randomize_out[] = { {SOCK_GEOMETRY, N_("Geometry")}, {-1, ""}, }; -static void geo_node_random_attribute_init(bNodeTree *UNUSED(tree), bNode *node) +static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node) { node->custom1 = CD_PROP_FLOAT; } -static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *node) +static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node) { bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2); bNodeSocket *sock_max_vector = sock_min_vector->next; @@ -59,38 +60,85 @@ static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *no namespace blender::nodes { -static void randomize_attribute(FloatWriteAttribute &attribute, - float min, - float max, - RandomNumberGenerator &rng) +/** Rehash to combine the seed with the "id" hash and a mutator for each dimension. */ +static float noise_from_index_and_mutator(const int seed, const int hash, const int mutator) +{ + const int combined_hash = BLI_hash_int_3d(seed, hash, mutator); + return BLI_hash_int_01(combined_hash); +} + +/** Rehash to combine the seed with the "id" hash. */ +static float noise_from_index(const int seed, const int hash) +{ + const int combined_hash = BLI_hash_int_2d(seed, hash); + return BLI_hash_int_01(combined_hash); +} + +static void randomize_attribute(BooleanWriteAttribute &attribute, Span<int> hashes, const int seed) +{ + MutableSpan<bool> attribute_span = attribute.get_span(); + for (const int i : IndexRange(attribute.size())) { + const bool value = noise_from_index(seed, hashes[i]) > 0.5f; + attribute_span[i] = value; + } + attribute.apply_span(); +} + +static void randomize_attribute( + FloatWriteAttribute &attribute, float min, float max, Span<int> hashes, const int seed) { MutableSpan<float> attribute_span = attribute.get_span(); for (const int i : IndexRange(attribute.size())) { - const float value = rng.get_float() * (max - min) + min; + const float value = noise_from_index(seed, hashes[i]) * (max - min) + min; attribute_span[i] = value; } attribute.apply_span(); } -static void randomize_attribute(Float3WriteAttribute &attribute, - float3 min, - float3 max, - RandomNumberGenerator &rng) +static void randomize_attribute( + Float3WriteAttribute &attribute, float3 min, float3 max, Span<int> hashes, const int seed) { MutableSpan<float3> attribute_span = attribute.get_span(); for (const int i : IndexRange(attribute.size())) { - const float x = rng.get_float(); - const float y = rng.get_float(); - const float z = rng.get_float(); + const float x = noise_from_index_and_mutator(seed, hashes[i], 47); + const float y = noise_from_index_and_mutator(seed, hashes[i], 8); + const float z = noise_from_index_and_mutator(seed, hashes[i], 64); const float3 value = float3(x, y, z) * (max - min) + min; attribute_span[i] = value; } attribute.apply_span(); } +static Array<int> get_element_hashes(GeometryComponent &component, + const AttributeDomain domain, + const int attribute_size) +{ + /* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */ + ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain); + Array<int> hashes(attribute_size); + if (hash_attribute) { + BLI_assert(hashes.size() == hash_attribute->size()); + const CPPType &cpp_type = hash_attribute->cpp_type(); + fn::GSpan items = hash_attribute->get_span(); + for (const int i : hashes.index_range()) { + hashes[i] = (int)cpp_type.hash(items[i]); + } + } + else { + /* If there is no "id" attribute for per-point variation, just create it here. */ + RandomNumberGenerator rng; + rng.seed(0); + for (const int i : hashes.index_range()) { + hashes[i] = rng.get_int32(); + } + } + + return hashes; +} + static void randomize_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms, - RandomNumberGenerator &rng) + const int seed) { const bNode &node = params.node(); const CustomDataType data_type = static_cast<CustomDataType>(node.custom1); @@ -106,19 +154,26 @@ static void randomize_attribute(GeometryComponent &component, return; } + Array<int> hashes = get_element_hashes(component, domain, attribute->size()); + switch (data_type) { case CD_PROP_FLOAT: { FloatWriteAttribute float_attribute = std::move(attribute); const float min_value = params.get_input<float>("Min_001"); const float max_value = params.get_input<float>("Max_001"); - randomize_attribute(float_attribute, min_value, max_value, rng); + randomize_attribute(float_attribute, min_value, max_value, hashes, seed); break; } case CD_PROP_FLOAT3: { Float3WriteAttribute float3_attribute = std::move(attribute); const float3 min_value = params.get_input<float3>("Min"); const float3 max_value = params.get_input<float3>("Max"); - randomize_attribute(float3_attribute, min_value, max_value, rng); + randomize_attribute(float3_attribute, min_value, max_value, hashes, seed); + break; + } + case CD_PROP_BOOL: { + BooleanWriteAttribute boolean_attribute = std::move(attribute); + randomize_attribute(boolean_attribute, hashes, seed); break; } default: @@ -132,14 +187,10 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params) const int seed = params.get_input<int>("Seed"); if (geometry_set.has<MeshComponent>()) { - RandomNumberGenerator rng; - rng.seed_random(seed); - randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, rng); + randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, seed); } if (geometry_set.has<PointCloudComponent>()) { - RandomNumberGenerator rng; - rng.seed_random(seed + 3245231); - randomize_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params, rng); + randomize_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params, seed); } params.set_output("Geometry", geometry_set); @@ -147,15 +198,16 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_random_attribute() +void register_node_type_geo_attribute_randomize() { static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_RANDOM_ATTRIBUTE, "Random Attribute", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates(&ntype, geo_node_random_attribute_in, geo_node_random_attribute_out); - node_type_init(&ntype, geo_node_random_attribute_init); - node_type_update(&ntype, geo_node_random_attribute_update); + &ntype, GEO_NODE_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_randomize_in, geo_node_attribute_randomize_out); + node_type_init(&ntype, geo_node_attribute_randomize_init); + node_type_update(&ntype, geo_node_attribute_randomize_update); ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index dedc3213a1f..80ac45aed4e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -218,12 +218,12 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>(); for (const InstancesComponent *component : src_components) { const int size = component->instances_amount(); - Span<const Object *> objects = component->objects(); + Span<InstancedData> instanced_data = component->instanced_data(); Span<float3> positions = component->positions(); Span<float3> rotations = component->rotations(); Span<float3> scales = component->scales(); for (const int i : IndexRange(size)) { - dst_component.add_instance(objects[i], positions[i], rotations[i], scales[i]); + dst_component.add_instance(instanced_data[i], positions[i], rotations[i], scales[i]); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index 2f5f7e264bc..1d3fbae5b2e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -24,6 +24,7 @@ #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" +#include "BKE_bvhutils.h" #include "BKE_deform.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -33,8 +34,10 @@ static bNodeSocketTemplate geo_node_point_distribute_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_FLOAT, N_("Density"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Distance Min"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Density Max"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE}, {SOCK_STRING, N_("Density Attribute")}, + {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000}, {-1, ""}, }; @@ -43,11 +46,20 @@ static bNodeSocketTemplate geo_node_point_distribute_out[] = { {-1, ""}, }; +static void node_point_distribute_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock_min_dist = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + + nodeSetSocketAvailability(sock_min_dist, ELEM(node->custom1, GEO_NODE_POINT_DISTRIBUTE_POISSON)); +} + namespace blender::nodes { -static Vector<float3> scatter_points_from_mesh(const Mesh *mesh, - const float density, - const FloatReadAttribute &density_factors) +static Vector<float3> random_scatter_points_from_mesh(const Mesh *mesh, + const float density, + const FloatReadAttribute &density_factors, + Vector<int> &r_ids, + const int seed) { /* This only updates a cache and can be considered to be logically const. */ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(mesh)); @@ -71,7 +83,7 @@ static Vector<float3> scatter_points_from_mesh(const Mesh *mesh, 3.0f; const float area = area_tri_v3(v0_pos, v1_pos, v2_pos); - const int looptri_seed = BLI_hash_int(looptri_index); + const int looptri_seed = BLI_hash_int(looptri_index + seed); RandomNumberGenerator looptri_rng(looptri_seed); const float points_amount_fl = area * density * looptri_density_factor; @@ -84,23 +96,178 @@ static Vector<float3> scatter_points_from_mesh(const Mesh *mesh, float3 point_pos; interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coords); points.append(point_pos); + + /* Build a hash stable even when the mesh is deformed. */ + r_ids.append(((int)(bary_coords.hash()) + looptri_index)); } } return points; } +struct RayCastAll_Data { + void *bvhdata; + + BVHTree_RayCastCallback raycast_callback; + + /** The original coordinate the result point was projected from. */ + float2 raystart; + + const Mesh *mesh; + float base_weight; + FloatReadAttribute *density_factors; + Vector<float3> *projected_points; + Vector<int> *stable_ids; + float cur_point_weight; +}; + +static void project_2d_bvh_callback(void *userdata, + int index, + const BVHTreeRay *ray, + BVHTreeRayHit *hit) +{ + struct RayCastAll_Data *data = (RayCastAll_Data *)userdata; + data->raycast_callback(data->bvhdata, index, ray, hit); + if (hit->index != -1) { + /* This only updates a cache and can be considered to be logically const. */ + const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(data->mesh)); + const MVert *mvert = data->mesh->mvert; + + const MLoopTri &looptri = looptris[index]; + const FloatReadAttribute &density_factors = data->density_factors[0]; + + const int v0_index = data->mesh->mloop[looptri.tri[0]].v; + const int v1_index = data->mesh->mloop[looptri.tri[1]].v; + const int v2_index = data->mesh->mloop[looptri.tri[2]].v; + + const float v0_density_factor = std::max(0.0f, density_factors[v0_index]); + const float v1_density_factor = std::max(0.0f, density_factors[v1_index]); + const float v2_density_factor = std::max(0.0f, density_factors[v2_index]); + + /* Calculate barycentric weights for hit point. */ + float3 weights; + interp_weights_tri_v3( + weights, mvert[v0_index].co, mvert[v1_index].co, mvert[v2_index].co, hit->co); + + float point_weight = weights[0] * v0_density_factor + weights[1] * v1_density_factor + + weights[2] * v2_density_factor; + + point_weight *= data->base_weight; + + if (point_weight >= FLT_EPSILON && data->cur_point_weight <= point_weight) { + data->projected_points->append(hit->co); + + /* Build a hash stable even when the mesh is deformed. */ + data->stable_ids->append((int)data->raystart.hash()); + } + } +} + +static Vector<float3> poisson_scatter_points_from_mesh(const Mesh *mesh, + const float density, + const float minimum_distance, + const FloatReadAttribute &density_factors, + Vector<int> &r_ids, + const int seed) +{ + Vector<float3> points; + + if (minimum_distance <= FLT_EPSILON || density <= FLT_EPSILON) { + return points; + } + + /* Scatter points randomly on the mesh with higher density (5-7) times higher than desired for + * good quality possion disk distributions. */ + int quality = 5; + const int output_points_target = 1000; + points.resize(output_points_target * quality); + + const float required_area = output_points_target * + (2.0f * sqrtf(3.0f) * minimum_distance * minimum_distance); + const float point_scale_multiplier = sqrtf(required_area); + + { + const int rnd_seed = BLI_hash_int(seed); + RandomNumberGenerator point_rng(rnd_seed); + + for (int i = 0; i < points.size(); i++) { + points[i].x = point_rng.get_float() * point_scale_multiplier; + points[i].y = point_rng.get_float() * point_scale_multiplier; + points[i].z = 0.0f; + } + } + + /* Eliminate the scattered points until we get a possion distribution. */ + Vector<float3> output_points(output_points_target); + + const float3 bounds_max = float3(point_scale_multiplier, point_scale_multiplier, 0); + poisson_disk_point_elimination(&points, &output_points, 2.0f * minimum_distance, bounds_max); + Vector<float3> final_points; + r_ids.reserve(output_points_target); + final_points.reserve(output_points_target); + + /* Check if we have any points we should remove from the final possion distribition. */ + BVHTreeFromMesh treedata; + BKE_bvhtree_from_mesh_get(&treedata, const_cast<Mesh *>(mesh), BVHTREE_FROM_LOOPTRI, 2); + + float3 bb_min, bb_max; + BLI_bvhtree_get_bounding_box(treedata.tree, bb_min, bb_max); + + struct RayCastAll_Data data; + data.bvhdata = &treedata; + data.raycast_callback = treedata.raycast_callback; + data.mesh = mesh; + data.projected_points = &final_points; + data.stable_ids = &r_ids; + data.density_factors = const_cast<FloatReadAttribute *>(&density_factors); + data.base_weight = std::min( + 1.0f, density / (output_points.size() / (point_scale_multiplier * point_scale_multiplier))); + + const float max_dist = bb_max[2] - bb_min[2] + 2.0f; + const float3 dir = float3(0, 0, -1); + float3 raystart; + raystart.z = bb_max[2] + 1.0f; + + float tile_start_x_coord = bb_min[0]; + int tile_repeat_x = ceilf((bb_max[0] - bb_min[0]) / point_scale_multiplier); + + float tile_start_y_coord = bb_min[1]; + int tile_repeat_y = ceilf((bb_max[1] - bb_min[1]) / point_scale_multiplier); + + for (int x = 0; x < tile_repeat_x; x++) { + float tile_curr_x_coord = x * point_scale_multiplier + tile_start_x_coord; + for (int y = 0; y < tile_repeat_y; y++) { + float tile_curr_y_coord = y * point_scale_multiplier + tile_start_y_coord; + for (int idx = 0; idx < output_points.size(); idx++) { + raystart.x = output_points[idx].x + tile_curr_x_coord; + raystart.y = output_points[idx].y + tile_curr_y_coord; + + data.cur_point_weight = (float)idx / (float)output_points.size(); + data.raystart = raystart; + + BLI_bvhtree_ray_cast_all( + treedata.tree, raystart, dir, 0.0f, max_dist, project_2d_bvh_callback, &data); + } + } + } + + return final_points; +} + static void geo_node_point_distribute_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet geometry_set_out; + GeometryNodePointDistributeMethod distribute_method = + static_cast<GeometryNodePointDistributeMethod>(params.node().custom1); + if (!geometry_set.has_mesh()) { params.set_output("Geometry", std::move(geometry_set_out)); return; } - const float density = params.extract_input<float>("Density"); + const float density = params.extract_input<float>("Density Max"); const std::string density_attribute = params.extract_input<std::string>("Density Attribute"); if (density <= 0.0f) { @@ -113,8 +280,21 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) const FloatReadAttribute density_factors = mesh_component.attribute_get_for_read<float>( density_attribute, ATTR_DOMAIN_POINT, 1.0f); + const int seed = params.get_input<int>("Seed"); - Vector<float3> points = scatter_points_from_mesh(mesh_in, density, density_factors); + Vector<int> stable_ids; + Vector<float3> points; + switch (distribute_method) { + case GEO_NODE_POINT_DISTRIBUTE_RANDOM: + points = random_scatter_points_from_mesh( + mesh_in, density, density_factors, stable_ids, seed); + break; + case GEO_NODE_POINT_DISTRIBUTE_POISSON: + const float min_dist = params.extract_input<float>("Distance Min"); + points = poisson_scatter_points_from_mesh( + mesh_in, density, min_dist, density_factors, stable_ids, seed); + break; + } PointCloud *pointcloud = BKE_pointcloud_new_nomain(points.size()); memcpy(pointcloud->co, points.data(), sizeof(float3) * points.size()); @@ -123,7 +303,16 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) pointcloud->radius[i] = 0.05f; } - geometry_set_out.replace_pointcloud(pointcloud); + PointCloudComponent &point_component = + geometry_set_out.get_component_for_write<PointCloudComponent>(); + point_component.replace(pointcloud); + + Int32WriteAttribute stable_id_attribute = point_component.attribute_try_ensure_for_write( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + MutableSpan<int> stable_ids_span = stable_id_attribute.get_span(); + stable_ids_span.copy_from(stable_ids); + stable_id_attribute.apply_span(); + params.set_output("Geometry", std::move(geometry_set_out)); } } // namespace blender::nodes @@ -135,6 +324,7 @@ void register_node_type_geo_point_distribute() geo_node_type_base( &ntype, GEO_NODE_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates(&ntype, geo_node_point_distribute_in, geo_node_point_distribute_out); + node_type_update(&ntype, node_point_distribute_update); ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute_poisson_disk.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute_poisson_disk.cc new file mode 100644 index 00000000000..47764efa15d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute_poisson_disk.cc @@ -0,0 +1,281 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Based on Cem Yuksel. 2015. Sample Elimination for Generating Poisson Disk Sample + * ! Sets. Computer Graphics Forum 34, 2 (May 2015), 25-32. + * ! http://www.cemyuksel.com/research/sampleelimination/ + * Copyright (c) 2016, Cem Yuksel <cem@cemyuksel.com> + * All rights reserved. + */ + +#include "BLI_inplace_priority_queue.hh" +#include "BLI_kdtree.h" + +#include "node_geometry_util.hh" + +#include <iostream> +#include <string.h> + +namespace blender::nodes { + +static void tile_point(Vector<float3> *tiled_points, + Vector<size_t> *indices, + const float maximum_distance, + const float3 boundbox, + float3 const &point, + size_t index, + int dimension = 0) +{ + for (int dimension_iter = dimension; dimension_iter < 3; dimension_iter++) { + if (boundbox[dimension_iter] - point[dimension_iter] < maximum_distance) { + float3 point_tiled = point; + point_tiled[dimension_iter] -= boundbox[dimension_iter]; + + tiled_points->append(point_tiled); + indices->append(index); + + tile_point(tiled_points, + indices, + maximum_distance, + boundbox, + point_tiled, + index, + dimension_iter + 1); + } + + if (point[dimension_iter] < maximum_distance) { + float3 point_tiled = point; + point_tiled[dimension_iter] += boundbox[dimension_iter]; + + tiled_points->append(point_tiled); + indices->append(index); + + tile_point(tiled_points, + indices, + maximum_distance, + boundbox, + point_tiled, + index, + dimension_iter + 1); + } + } +} + +/** + * Returns the weight the point gets based on the distance to another point. + */ +static float point_weight_influence_get(const float maximum_distance, + const float minimum_distance, + float distance) +{ + const float alpha = 8.0f; + + if (distance < minimum_distance) { + distance = minimum_distance; + } + + return std::pow(1.0f - distance / maximum_distance, alpha); +} + +/** + * Weight each point based on their proximity to its neighbors + * + * For each index in the weight array add a weight based on the proximity the + * corresponding point has with its neighboors. + **/ +static void points_distance_weight_calculate(Vector<float> *weights, + const size_t point_id, + const float3 *input_points, + const void *kd_tree, + const float minimum_distance, + const float maximum_distance, + InplacePriorityQueue<float> *heap) +{ + KDTreeNearest_3d *nearest_point = nullptr; + int neighbors = BLI_kdtree_3d_range_search( + (KDTree_3d *)kd_tree, input_points[point_id], &nearest_point, maximum_distance); + + for (int i = 0; i < neighbors; i++) { + size_t neighbor_point_id = nearest_point[i].index; + + if (neighbor_point_id >= weights->size()) { + continue; + } + + /* The point should not influence itself. */ + if (neighbor_point_id == point_id) { + continue; + } + + const float weight_influence = point_weight_influence_get( + maximum_distance, minimum_distance, nearest_point[i].dist); + + /* In the first pass we just the weights. */ + if (heap == nullptr) { + (*weights)[point_id] += weight_influence; + } + /* When we run again we need to update the weights and the heap. */ + else { + (*weights)[neighbor_point_id] -= weight_influence; + heap->priority_decreased(neighbor_point_id); + } + } + + if (nearest_point) { + MEM_freeN(nearest_point); + } +} + +/** + * Returns the minimum radius fraction used by the default weight function. + */ +static float weight_limit_fraction_get(const size_t input_size, const size_t output_size) +{ + const float beta = 0.65f; + const float gamma = 1.5f; + float ratio = float(output_size) / float(input_size); + return (1.0f - std::pow(ratio, gamma)) * beta; +} + +/** + * Tile the input points. + */ +static void points_tiling(const float3 *input_points, + const size_t input_size, + void **kd_tree, + const float maximum_distance, + const float3 boundbox) + +{ + Vector<float3> tiled_points(input_points, input_points + input_size); + Vector<size_t> indices(input_size); + + for (size_t i = 0; i < input_size; i++) { + indices[i] = i; + } + + /* Tile the tree based on the boundbox. */ + for (size_t i = 0; i < input_size; i++) { + tile_point(&tiled_points, &indices, maximum_distance, boundbox, input_points[i], i); + } + + /* Build a new tree with the new indices and tiled points. */ + *kd_tree = BLI_kdtree_3d_new(tiled_points.size()); + for (size_t i = 0; i < tiled_points.size(); i++) { + BLI_kdtree_3d_insert(*(KDTree_3d **)kd_tree, indices[i], tiled_points[i]); + } + BLI_kdtree_3d_balance(*(KDTree_3d **)kd_tree); +} + +static void weighted_sample_elimination(const float3 *input_points, + const size_t input_size, + float3 *output_points, + const size_t output_size, + const float maximum_distance, + const float3 boundbox, + const bool do_copy_eliminated) +{ + const float minimum_distance = maximum_distance * + weight_limit_fraction_get(input_size, output_size); + + void *kd_tree = nullptr; + points_tiling(input_points, input_size, &kd_tree, maximum_distance, boundbox); + + /* Assign weights to each sample. */ + Vector<float> weights(input_size, 0.0f); + for (size_t point_id = 0; point_id < weights.size(); point_id++) { + points_distance_weight_calculate( + &weights, point_id, input_points, kd_tree, minimum_distance, maximum_distance, nullptr); + } + + /* Remove the points based on their weight. */ + InplacePriorityQueue<float> heap(weights); + + size_t sample_size = input_size; + while (sample_size > output_size) { + /* For each sample around it, remove its weight contribution and update the heap. */ + size_t point_id = heap.pop_index(); + points_distance_weight_calculate( + &weights, point_id, input_points, kd_tree, minimum_distance, maximum_distance, &heap); + sample_size--; + } + + /* Copy the samples to the output array. */ + size_t target_size = do_copy_eliminated ? input_size : output_size; + for (size_t i = 0; i < target_size; i++) { + size_t index = heap.all_indices()[i]; + output_points[i] = input_points[index]; + } + + /* Cleanup. */ + BLI_kdtree_3d_free((KDTree_3d *)kd_tree); +} + +static void progressive_sampling_reorder(Vector<float3> *output_points, + float maximum_density, + float3 boundbox) +{ + /* Re-order the points for progressive sampling. */ + Vector<float3> temporary_points(output_points->size()); + float3 *source_points = output_points->data(); + float3 *dest_points = temporary_points.data(); + size_t source_size = output_points->size(); + size_t dest_size = 0; + + while (source_size >= 3) { + dest_size = source_size * 0.5f; + + /* Changes the weight function radius using half of the number of samples. + * It is used for progressive sampling. */ + maximum_density *= std::sqrt(2.0f); + weighted_sample_elimination( + source_points, source_size, dest_points, dest_size, maximum_density, boundbox, true); + + if (dest_points != output_points->data()) { + memcpy((*output_points)[dest_size], + dest_points[dest_size], + (source_size - dest_size) * sizeof(float3)); + } + + /* Swap the arrays around. */ + float3 *points_iter = source_points; + source_points = dest_points; + dest_points = points_iter; + source_size = dest_size; + } + if (source_points != output_points->data()) { + memcpy(output_points->data(), source_points, dest_size * sizeof(float3)); + } +} + +void poisson_disk_point_elimination(Vector<float3> const *input_points, + Vector<float3> *output_points, + float maximum_density, + float3 boundbox) +{ + weighted_sample_elimination(input_points->data(), + input_points->size(), + output_points->data(), + output_points->size(), + maximum_density, + boundbox, + false); + + progressive_sampling_reorder(output_points, maximum_density, boundbox); +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 6d979e3b7da..e030bc3eec6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -25,6 +25,7 @@ static bNodeSocketTemplate geo_node_point_instance_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, {SOCK_OBJECT, N_("Object")}, + {SOCK_COLLECTION, N_("Collection")}, {-1, ""}, }; @@ -35,9 +36,21 @@ static bNodeSocketTemplate geo_node_point_instance_out[] = { namespace blender::nodes { +static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) +{ + bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + bNodeSocket *collection_socket = object_socket->next; + + GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node->custom1; + + nodeSetSocketAvailability(object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT); + nodeSetSocketAvailability(collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION); +} + static void add_instances_from_geometry_component(InstancesComponent &instances, const GeometryComponent &src_geometry, - Object *object) + Object *object, + Collection *collection) { Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); @@ -47,30 +60,51 @@ static void add_instances_from_geometry_component(InstancesComponent &instances, "scale", ATTR_DOMAIN_POINT, {1, 1, 1}); for (const int i : IndexRange(positions.size())) { - instances.add_instance(object, positions[i], rotations[i], scales[i]); + if (object != nullptr) { + instances.add_instance(object, positions[i], rotations[i], scales[i]); + } + if (collection != nullptr) { + instances.add_instance(collection, positions[i], rotations[i], scales[i]); + } } } static void geo_node_point_instance_exec(GeoNodeExecParams params) { + GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)params.node().custom1; GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet geometry_set_out; - bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>( - "Object"); - Object *object = params.handle_map().lookup(object_handle); + Object *object = nullptr; + Collection *collection = nullptr; - if (object != nullptr && object != params.self_object()) { - InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); - if (geometry_set.has<MeshComponent>()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read<MeshComponent>(), object); - } - if (geometry_set.has<PointCloudComponent>()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read<PointCloudComponent>(), object); + if (type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT) { + bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>( + "Object"); + object = params.handle_map().lookup(object_handle); + /* Avoid accidental recursion of instances. */ + if (object == params.self_object()) { + object = nullptr; } } + else if (type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION) { + bke::PersistentCollectionHandle collection_handle = + params.extract_input<bke::PersistentCollectionHandle>("Collection"); + collection = params.handle_map().lookup(collection_handle); + } + + InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); + if (geometry_set.has<MeshComponent>()) { + add_instances_from_geometry_component( + instances, *geometry_set.get_component_for_read<MeshComponent>(), object, collection); + } + if (geometry_set.has<PointCloudComponent>()) { + add_instances_from_geometry_component( + instances, + *geometry_set.get_component_for_read<PointCloudComponent>(), + object, + collection); + } params.set_output("Geometry", std::move(geometry_set_out)); } @@ -82,6 +116,7 @@ void register_node_type_geo_point_instance() geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out); + node_type_update(&ntype, blender::nodes::geo_node_point_instance_update); ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc index cc5e9547a96..5a6faa809f2 100644 --- a/source/blender/nodes/intern/math_functions.cc +++ b/source/blender/nodes/intern/math_functions.cc @@ -41,6 +41,8 @@ const FloatMathOperationInfo *get_float_math_operation_info(const int operation) RETURN_OPERATION_INFO("Sine", "math_sine"); case NODE_MATH_COSINE: RETURN_OPERATION_INFO("Cosine", "math_cosine"); + case NODE_MATH_TANGENT: + RETURN_OPERATION_INFO("Tangent", "math_tangent"); case NODE_MATH_ARCSINE: RETURN_OPERATION_INFO("Arc Sine", "math_arcsine"); case NODE_MATH_ARCCOSINE: diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index a6d9115f01f..eef2c6c9125 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -19,23 +19,31 @@ namespace blender::nodes { -ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const CustomDataType type, - const void *default_value) const +const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { - const bNodeSocket *found_socket = nullptr; LISTBASE_FOREACH (const bNodeSocket *, socket, &node_.inputs) { if ((socket->flag & SOCK_UNAVAIL) != 0) { continue; } if (name == socket->name) { - found_socket = socket; - break; + return socket; } } - BLI_assert(found_socket != nullptr); + + return nullptr; +} + +ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name, + const GeometryComponent &component, + const AttributeDomain domain, + const CustomDataType type, + const void *default_value) const +{ + const bNodeSocket *found_socket = this->find_available_socket(name); + BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ + if (found_socket == nullptr) { + return component.attribute_get_constant_for_read(domain, type, default_value); + } if (found_socket->type == SOCK_STRING) { const std::string name = this->get_input<std::string>(found_socket->identifier); @@ -60,6 +68,42 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name, return component.attribute_get_constant_for_read(domain, type, default_value); } +CustomDataType GeoNodeExecParams::get_input_attribute_data_type( + const StringRef name, + const GeometryComponent &component, + const CustomDataType default_type) const +{ + const bNodeSocket *found_socket = this->find_available_socket(name); + BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ + if (found_socket == nullptr) { + return default_type; + } + + if (found_socket->type == SOCK_STRING) { + const std::string name = this->get_input<std::string>(found_socket->identifier); + ReadAttributePtr attribute = component.attribute_try_get_for_read(name); + if (!attribute) { + return default_type; + } + return attribute->custom_data_type(); + } + if (found_socket->type == SOCK_FLOAT) { + return CD_PROP_FLOAT; + } + if (found_socket->type == SOCK_VECTOR) { + return CD_PROP_FLOAT3; + } + if (found_socket->type == SOCK_RGBA) { + return CD_PROP_COLOR; + } + if (found_socket->type == SOCK_BOOLEAN) { + return CD_PROP_BOOL; + } + + BLI_assert(false); + return default_type; +} + void GeoNodeExecParams::check_extract_input(StringRef identifier, const CPPType *requested_type) const { diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index ebc70956147..60c2d6c37e1 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -37,6 +37,8 @@ #include "BKE_node.h" #include "BKE_persistent_data_handle.hh" +#include "DNA_collection_types.h" + #include "RNA_access.h" #include "RNA_types.h" @@ -280,6 +282,15 @@ void node_socket_init_default_value(bNodeSocket *sock) sock->default_value = dval; break; } + case SOCK_COLLECTION: { + bNodeSocketValueCollection *dval = (bNodeSocketValueCollection *)MEM_callocN( + sizeof(bNodeSocketValueCollection), "node socket value object"); + dval->value = nullptr; + + sock->default_value = dval; + break; + break; + } } } @@ -352,6 +363,13 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from) id_us_plus(&toval->value->id); break; } + case SOCK_COLLECTION: { + bNodeSocketValueCollection *toval = (bNodeSocketValueCollection *)to->default_value; + bNodeSocketValueCollection *fromval = (bNodeSocketValueCollection *)from->default_value; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } } to->flag |= (from->flag & SOCK_HIDE_VALUE); @@ -648,6 +666,7 @@ class ObjectSocketMultiFunction : public blender::fn::MultiFunction { }; MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle); +MAKE_CPP_TYPE(PersistentCollectionHandle, blender::bke::PersistentCollectionHandle); static bNodeSocketType *make_socket_type_object() { @@ -673,6 +692,16 @@ static bNodeSocketType *make_socket_type_geometry() return socktype; } +static bNodeSocketType *make_socket_type_collection() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE); + socktype->get_cpp_type = []() { + /* Objects are not passed along as raw pointers, but as handles. */ + return &blender::fn::CPPType::get<blender::bke::PersistentCollectionHandle>(); + }; + return socktype; +} + void register_standard_node_socket_types(void) { /* draw callbacks are set in drawnode.c to avoid bad-level calls */ @@ -711,5 +740,7 @@ void register_standard_node_socket_types(void) nodeRegisterSocketType(make_socket_type_geometry()); + nodeRegisterSocketType(make_socket_type_collection()); + nodeRegisterSocketType(make_socket_type_virtual()); } diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc index ec5527a2970..2e4196af156 100644 --- a/source/blender/nodes/intern/node_tree_multi_function.cc +++ b/source/blender/nodes/intern/node_tree_multi_function.cc @@ -208,6 +208,10 @@ static DataTypeConversions create_implicit_conversions() conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); }); add_implicit_conversion<Color4f, float>( conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); }); + add_implicit_conversion<float3, bool>( + conversions, "float3 to boolean", [](float3 a) { return a.length_squared() == 0.0f; }); + add_implicit_conversion<bool, float3>( + conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); }); return conversions; } diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 123347afc19..9669dc6496b 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -451,6 +451,14 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype return -1; } } + case SOCK_COLLECTION: { + switch (from) { + case SOCK_COLLECTION: + return 1; + default: + return -1; + } + } default: return -1; } diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index 46b4dbc96d7..366d801a888 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -26,7 +26,6 @@ struct ID; /* DNA_ID.h */ struct ListBase; /* DNA_listBase.h */ struct Object; /* DNA_object_types.h */ struct PathResolvedRNA; -struct ReportList; struct Text; /* defined in DNA_text_types.h */ struct bConstraint; /* DNA_constraint_types.h */ struct bConstraintOb; /* DNA_constraint_types.h */ diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 63fb685e66d..fd996c8a1a2 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -28,6 +28,8 @@ #include "BKE_idprop.h" +#include "DNA_ID.h" /* ID property definitions. */ + #define USE_STRING_COERCE #ifdef USE_STRING_COERCE diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c index 81d1cb7055d..9fb7aea162a 100644 --- a/source/blender/python/gpu/gpu_py_batch.c +++ b/source/blender/python/gpu/gpu_py_batch.c @@ -48,7 +48,7 @@ /** \name Utility Functions * \{ */ -static bool bpygpu_batch_is_program_or_error(BPyGPUBatch *self) +static bool py_batch_is_program_or_error(BPyGPUBatch *self) { if (!self->batch->shader) { PyErr_SetString(PyExc_RuntimeError, "batch does not have any program assigned to it"); @@ -63,7 +63,7 @@ static bool bpygpu_batch_is_program_or_error(BPyGPUBatch *self) /** \name GPUBatch Type * \{ */ -static PyObject *bpygpu_Batch_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) +static PyObject *py_Batch_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { BPYGPU_IS_INIT_OR_ERROR_OBJ; @@ -121,7 +121,7 @@ static PyObject *bpygpu_Batch_new(PyTypeObject *UNUSED(type), PyObject *args, Py return (PyObject *)ret; } -PyDoc_STRVAR(bpygpu_Batch_vertbuf_add_doc, +PyDoc_STRVAR(py_Batch_vertbuf_add_doc, ".. method:: vertbuf_add(buf)\n" "\n" " Add another vertex buffer to the Batch.\n" @@ -134,7 +134,7 @@ PyDoc_STRVAR(bpygpu_Batch_vertbuf_add_doc, " :param buf: The vertex buffer that will be added to the batch.\n" " :type buf: :class:`gpu.types.GPUVertBuf`\n" ); -static PyObject *bpygpu_Batch_vertbuf_add(BPyGPUBatch *self, BPyGPUVertBuf *py_buf) +static PyObject *py_Batch_vertbuf_add(BPyGPUBatch *self, BPyGPUVertBuf *py_buf) { if (!BPyGPUVertBuf_Check(py_buf)) { PyErr_Format(PyExc_TypeError, "Expected a GPUVertBuf, got %s", Py_TYPE(py_buf)->tp_name); @@ -167,7 +167,7 @@ static PyObject *bpygpu_Batch_vertbuf_add(BPyGPUBatch *self, BPyGPUVertBuf *py_b } PyDoc_STRVAR( - bpygpu_Batch_program_set_doc, + py_Batch_program_set_doc, ".. method:: program_set(program)\n" "\n" " Assign a shader to this batch that will be used for drawing when not overwritten later.\n" @@ -177,7 +177,7 @@ PyDoc_STRVAR( "\n" " :param program: The program/shader the batch will use in future draw calls.\n" " :type program: :class:`gpu.types.GPUShader`\n"); -static PyObject *bpygpu_Batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_shader) +static PyObject *py_Batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_shader) { if (!BPyGPUShader_Check(py_shader)) { PyErr_Format(PyExc_TypeError, "Expected a GPUShader, got %s", Py_TYPE(py_shader)->tp_name); @@ -208,7 +208,7 @@ static PyObject *bpygpu_Batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_sh Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_Batch_draw_doc, +PyDoc_STRVAR(py_Batch_draw_doc, ".. method:: draw(program=None)\n" "\n" " Run the drawing program with the parameters assigned to the batch.\n" @@ -216,7 +216,7 @@ PyDoc_STRVAR(bpygpu_Batch_draw_doc, " :param program: Program that performs the drawing operations.\n" " If ``None`` is passed, the last program set to this batch will run.\n" " :type program: :class:`gpu.types.GPUShader`\n"); -static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args) +static PyObject *py_Batch_draw(BPyGPUBatch *self, PyObject *args) { BPyGPUShader *py_program = NULL; @@ -224,7 +224,7 @@ static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args) return NULL; } if (py_program == NULL) { - if (!bpygpu_batch_is_program_or_error(self)) { + if (!py_batch_is_program_or_error(self)) { return NULL; } } @@ -236,42 +236,42 @@ static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args) Py_RETURN_NONE; } -static PyObject *bpygpu_Batch_program_use_begin(BPyGPUBatch *self) +static PyObject *py_Batch_program_use_begin(BPyGPUBatch *self) { - if (!bpygpu_batch_is_program_or_error(self)) { + if (!py_batch_is_program_or_error(self)) { return NULL; } GPU_shader_bind(self->batch->shader); Py_RETURN_NONE; } -static PyObject *bpygpu_Batch_program_use_end(BPyGPUBatch *self) +static PyObject *py_Batch_program_use_end(BPyGPUBatch *self) { - if (!bpygpu_batch_is_program_or_error(self)) { + if (!py_batch_is_program_or_error(self)) { return NULL; } GPU_shader_unbind(); Py_RETURN_NONE; } -static struct PyMethodDef bpygpu_Batch_methods[] = { - {"vertbuf_add", (PyCFunction)bpygpu_Batch_vertbuf_add, METH_O, bpygpu_Batch_vertbuf_add_doc}, - {"program_set", (PyCFunction)bpygpu_Batch_program_set, METH_O, bpygpu_Batch_program_set_doc}, - {"draw", (PyCFunction)bpygpu_Batch_draw, METH_VARARGS, bpygpu_Batch_draw_doc}, - {"_program_use_begin", (PyCFunction)bpygpu_Batch_program_use_begin, METH_NOARGS, ""}, - {"_program_use_end", (PyCFunction)bpygpu_Batch_program_use_end, METH_NOARGS, ""}, +static struct PyMethodDef py_Batch_methods[] = { + {"vertbuf_add", (PyCFunction)py_Batch_vertbuf_add, METH_O, py_Batch_vertbuf_add_doc}, + {"program_set", (PyCFunction)py_Batch_program_set, METH_O, py_Batch_program_set_doc}, + {"draw", (PyCFunction)py_Batch_draw, METH_VARARGS, py_Batch_draw_doc}, + {"_program_use_begin", (PyCFunction)py_Batch_program_use_begin, METH_NOARGS, ""}, + {"_program_use_end", (PyCFunction)py_Batch_program_use_end, METH_NOARGS, ""}, {NULL, NULL, 0, NULL}, }; #ifdef USE_GPU_PY_REFERENCES -static int bpygpu_Batch_traverse(BPyGPUBatch *self, visitproc visit, void *arg) +static int py_Batch_traverse(BPyGPUBatch *self, visitproc visit, void *arg) { Py_VISIT(self->references); return 0; } -static int bpygpu_Batch_clear(BPyGPUBatch *self) +static int py_Batch_clear(BPyGPUBatch *self) { Py_CLEAR(self->references); return 0; @@ -279,14 +279,14 @@ static int bpygpu_Batch_clear(BPyGPUBatch *self) #endif -static void bpygpu_Batch_dealloc(BPyGPUBatch *self) +static void py_Batch_dealloc(BPyGPUBatch *self) { GPU_batch_discard(self->batch); #ifdef USE_GPU_PY_REFERENCES if (self->references) { PyObject_GC_UnTrack(self); - bpygpu_Batch_clear(self); + py_Batch_clear(self); Py_XDECREF(self->references); } #endif @@ -319,17 +319,17 @@ PyDoc_STRVAR( PyTypeObject BPyGPUBatch_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUBatch", .tp_basicsize = sizeof(BPyGPUBatch), - .tp_dealloc = (destructor)bpygpu_Batch_dealloc, + .tp_dealloc = (destructor)py_Batch_dealloc, #ifdef USE_GPU_PY_REFERENCES .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_doc = py_gpu_batch_doc, - .tp_traverse = (traverseproc)bpygpu_Batch_traverse, - .tp_clear = (inquiry)bpygpu_Batch_clear, + .tp_traverse = (traverseproc)py_Batch_traverse, + .tp_clear = (inquiry)py_Batch_clear, #else .tp_flags = Py_TPFLAGS_DEFAULT, #endif - .tp_methods = bpygpu_Batch_methods, - .tp_new = bpygpu_Batch_new, + .tp_methods = py_Batch_methods, + .tp_new = py_Batch_new, }; /** \} */ diff --git a/source/blender/python/gpu/gpu_py_element.c b/source/blender/python/gpu/gpu_py_element.c index c863c9a586f..0a1aecde986 100644 --- a/source/blender/python/gpu/gpu_py_element.c +++ b/source/blender/python/gpu/gpu_py_element.c @@ -39,7 +39,7 @@ /** \name IndexBuf Type * \{ */ -static PyObject *bpygpu_IndexBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) +static PyObject *py_IndexBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { BPYGPU_IS_INIT_OR_ERROR_OBJ; @@ -175,7 +175,7 @@ static PyObject *bpygpu_IndexBuf_new(PyTypeObject *UNUSED(type), PyObject *args, return BPyGPUIndexBuf_CreatePyObject(GPU_indexbuf_build(&builder)); } -static void bpygpu_IndexBuf_dealloc(BPyGPUIndexBuf *self) +static void py_IndexBuf_dealloc(BPyGPUIndexBuf *self) { GPU_indexbuf_discard(self->elem); Py_TYPE(self)->tp_free(self); @@ -199,10 +199,10 @@ PyDoc_STRVAR(py_gpu_element_doc, PyTypeObject BPyGPUIndexBuf_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUIndexBuf", .tp_basicsize = sizeof(BPyGPUIndexBuf), - .tp_dealloc = (destructor)bpygpu_IndexBuf_dealloc, + .tp_dealloc = (destructor)py_IndexBuf_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = py_gpu_element_doc, - .tp_new = bpygpu_IndexBuf_new, + .tp_new = py_IndexBuf_new, }; /** \} */ diff --git a/source/blender/python/gpu/gpu_py_matrix.c b/source/blender/python/gpu/gpu_py_matrix.c index 93cca78bad9..a479f270770 100644 --- a/source/blender/python/gpu/gpu_py_matrix.c +++ b/source/blender/python/gpu/gpu_py_matrix.c @@ -44,7 +44,7 @@ /** \name Helper Functions * \{ */ -static bool bpygpu_stack_is_push_model_view_ok_or_error(void) +static bool py_stack_is_push_model_view_ok_or_error(void) { if (GPU_matrix_stack_level_get_model_view() >= GPU_PY_MATRIX_STACK_LEN) { PyErr_SetString( @@ -55,7 +55,7 @@ static bool bpygpu_stack_is_push_model_view_ok_or_error(void) return true; } -static bool bpygpu_stack_is_push_projection_ok_or_error(void) +static bool py_stack_is_push_projection_ok_or_error(void) { if (GPU_matrix_stack_level_get_projection() >= GPU_PY_MATRIX_STACK_LEN) { PyErr_SetString( @@ -66,7 +66,7 @@ static bool bpygpu_stack_is_push_projection_ok_or_error(void) return true; } -static bool bpygpu_stack_is_pop_model_view_ok_or_error(void) +static bool py_stack_is_pop_model_view_ok_or_error(void) { if (GPU_matrix_stack_level_get_model_view() == 0) { PyErr_SetString(PyExc_RuntimeError, "Minimum model-view stack depth reached"); @@ -75,7 +75,7 @@ static bool bpygpu_stack_is_pop_model_view_ok_or_error(void) return true; } -static bool bpygpu_stack_is_pop_projection_ok_or_error(void) +static bool py_stack_is_pop_projection_ok_or_error(void) { if (GPU_matrix_stack_level_get_projection() == 0) { PyErr_SetString(PyExc_RuntimeError, "Minimum projection stack depth reached"); @@ -90,52 +90,52 @@ static bool bpygpu_stack_is_pop_projection_ok_or_error(void) /** \name Manage Stack * \{ */ -PyDoc_STRVAR(bpygpu_matrix_push_doc, +PyDoc_STRVAR(py_matrix_push_doc, ".. function:: push()\n" "\n" " Add to the model-view matrix stack.\n"); -static PyObject *bpygpu_matrix_push(PyObject *UNUSED(self)) +static PyObject *py_matrix_push(PyObject *UNUSED(self)) { - if (!bpygpu_stack_is_push_model_view_ok_or_error()) { + if (!py_stack_is_push_model_view_ok_or_error()) { return NULL; } GPU_matrix_push(); Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_matrix_pop_doc, +PyDoc_STRVAR(py_matrix_pop_doc, ".. function:: pop()\n" "\n" " Remove the last model-view matrix from the stack.\n"); -static PyObject *bpygpu_matrix_pop(PyObject *UNUSED(self)) +static PyObject *py_matrix_pop(PyObject *UNUSED(self)) { - if (!bpygpu_stack_is_pop_model_view_ok_or_error()) { + if (!py_stack_is_pop_model_view_ok_or_error()) { return NULL; } GPU_matrix_pop(); Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_matrix_push_projection_doc, +PyDoc_STRVAR(py_matrix_push_projection_doc, ".. function:: push_projection()\n" "\n" " Add to the projection matrix stack.\n"); -static PyObject *bpygpu_matrix_push_projection(PyObject *UNUSED(self)) +static PyObject *py_matrix_push_projection(PyObject *UNUSED(self)) { - if (!bpygpu_stack_is_push_projection_ok_or_error()) { + if (!py_stack_is_push_projection_ok_or_error()) { return NULL; } GPU_matrix_push_projection(); Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_matrix_pop_projection_doc, +PyDoc_STRVAR(py_matrix_pop_projection_doc, ".. function:: pop_projection()\n" "\n" " Remove the last projection matrix from the stack.\n"); -static PyObject *bpygpu_matrix_pop_projection(PyObject *UNUSED(self)) +static PyObject *py_matrix_pop_projection(PyObject *UNUSED(self)) { - if (!bpygpu_stack_is_pop_projection_ok_or_error()) { + if (!py_stack_is_pop_projection_ok_or_error()) { return NULL; } GPU_matrix_pop_projection(); @@ -162,12 +162,12 @@ enum { PYGPU_MATRIX_TYPE_PROJECTION = 2, }; -static PyObject *bpygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self); -static PyObject *bpygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, PyObject *args); +static PyObject *py_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self); +static PyObject *py_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, PyObject *args); -static PyMethodDef bpygpu_matrix_stack_context_methods[] = { - {"__enter__", (PyCFunction)bpygpu_matrix_stack_context_enter, METH_NOARGS}, - {"__exit__", (PyCFunction)bpygpu_matrix_stack_context_exit, METH_VARARGS}, +static PyMethodDef py_matrix_stack_context_methods[] = { + {"__enter__", (PyCFunction)py_matrix_stack_context_enter, METH_NOARGS}, + {"__exit__", (PyCFunction)py_matrix_stack_context_exit, METH_VARARGS}, {NULL}, }; @@ -175,10 +175,10 @@ static PyTypeObject BPyGPU_matrix_stack_context_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUMatrixStackContext", .tp_basicsize = sizeof(BPyGPU_MatrixStackContext), .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_methods = bpygpu_matrix_stack_context_methods, + .tp_methods = py_matrix_stack_context_methods, }; -static PyObject *bpygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self) +static PyObject *py_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self) { /* sanity - should never happen */ if (self->level != -1) { @@ -187,14 +187,14 @@ static PyObject *bpygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *se } if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) { - if (!bpygpu_stack_is_push_model_view_ok_or_error()) { + if (!py_stack_is_push_model_view_ok_or_error()) { return NULL; } GPU_matrix_push(); self->level = GPU_matrix_stack_level_get_model_view(); } else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) { - if (!bpygpu_stack_is_push_projection_ok_or_error()) { + if (!py_stack_is_push_projection_ok_or_error()) { return NULL; } GPU_matrix_push_projection(); @@ -206,8 +206,8 @@ static PyObject *bpygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *se Py_RETURN_NONE; } -static PyObject *bpygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, - PyObject *UNUSED(args)) +static PyObject *py_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, + PyObject *UNUSED(args)) { /* sanity - should never happen */ if (self->level == -1) { @@ -240,7 +240,7 @@ finally: Py_RETURN_NONE; } -static PyObject *bpygpu_matrix_push_pop_impl(int type) +static PyObject *py_matrix_push_pop_impl(int type) { BPyGPU_MatrixStackContext *ret = PyObject_New(BPyGPU_MatrixStackContext, &BPyGPU_matrix_stack_context_Type); @@ -250,23 +250,23 @@ static PyObject *bpygpu_matrix_push_pop_impl(int type) } PyDoc_STRVAR( - bpygpu_matrix_push_pop_doc, + py_matrix_push_pop_doc, ".. function:: push_pop()\n" "\n" " Context manager to ensure balanced push/pop calls, even in the case of an error.\n"); -static PyObject *bpygpu_matrix_push_pop(PyObject *UNUSED(self)) +static PyObject *py_matrix_push_pop(PyObject *UNUSED(self)) { - return bpygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_MODEL_VIEW); + return py_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_MODEL_VIEW); } PyDoc_STRVAR( - bpygpu_matrix_push_pop_projection_doc, + py_matrix_push_pop_projection_doc, ".. function:: push_pop_projection()\n" "\n" " Context manager to ensure balanced push/pop calls, even in the case of an error.\n"); -static PyObject *bpygpu_matrix_push_pop_projection(PyObject *UNUSED(self)) +static PyObject *py_matrix_push_pop_projection(PyObject *UNUSED(self)) { - return bpygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_PROJECTION); + return py_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_PROJECTION); } /** \} */ @@ -275,14 +275,14 @@ static PyObject *bpygpu_matrix_push_pop_projection(PyObject *UNUSED(self)) /** \name Manipulate State * \{ */ -PyDoc_STRVAR(bpygpu_matrix_multiply_matrix_doc, +PyDoc_STRVAR(py_matrix_multiply_matrix_doc, ".. function:: multiply_matrix(matrix)\n" "\n" " Multiply the current stack matrix.\n" "\n" " :param matrix: A 4x4 matrix.\n" " :type matrix: :class:`mathutils.Matrix`\n"); -static PyObject *bpygpu_matrix_multiply_matrix(PyObject *UNUSED(self), PyObject *value) +static PyObject *py_matrix_multiply_matrix(PyObject *UNUSED(self), PyObject *value) { MatrixObject *pymat; if (!Matrix_Parse4x4(value, &pymat)) { @@ -292,14 +292,14 @@ static PyObject *bpygpu_matrix_multiply_matrix(PyObject *UNUSED(self), PyObject Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_matrix_scale_doc, +PyDoc_STRVAR(py_matrix_scale_doc, ".. function:: scale(scale)\n" "\n" " Scale the current stack matrix.\n" "\n" " :param scale: Scale the current stack matrix.\n" " :type scale: sequence of 2 or 3 floats\n"); -static PyObject *bpygpu_matrix_scale(PyObject *UNUSED(self), PyObject *value) +static PyObject *py_matrix_scale(PyObject *UNUSED(self), PyObject *value) { float scale[3]; int len; @@ -316,12 +316,12 @@ static PyObject *bpygpu_matrix_scale(PyObject *UNUSED(self), PyObject *value) Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_matrix_scale_uniform_doc, +PyDoc_STRVAR(py_matrix_scale_uniform_doc, ".. function:: scale_uniform(scale)\n" "\n" " :param scale: Scale the current stack matrix.\n" " :type scale: float\n"); -static PyObject *bpygpu_matrix_scale_uniform(PyObject *UNUSED(self), PyObject *value) +static PyObject *py_matrix_scale_uniform(PyObject *UNUSED(self), PyObject *value) { float scalar; if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) { @@ -332,14 +332,14 @@ static PyObject *bpygpu_matrix_scale_uniform(PyObject *UNUSED(self), PyObject *v Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_matrix_translate_doc, +PyDoc_STRVAR(py_matrix_translate_doc, ".. function:: translate(offset)\n" "\n" " Scale the current stack matrix.\n" "\n" " :param offset: Translate the current stack matrix.\n" " :type offset: sequence of 2 or 3 floats\n"); -static PyObject *bpygpu_matrix_translate(PyObject *UNUSED(self), PyObject *value) +static PyObject *py_matrix_translate(PyObject *UNUSED(self), PyObject *value) { float offset[3]; int len; @@ -362,34 +362,34 @@ static PyObject *bpygpu_matrix_translate(PyObject *UNUSED(self), PyObject *value /** \name Write State * \{ */ -PyDoc_STRVAR(bpygpu_matrix_reset_doc, +PyDoc_STRVAR(py_matrix_reset_doc, ".. function:: reset()\n" "\n" " Empty stack and set to identity.\n"); -static PyObject *bpygpu_matrix_reset(PyObject *UNUSED(self)) +static PyObject *py_matrix_reset(PyObject *UNUSED(self)) { GPU_matrix_reset(); Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_matrix_load_identity_doc, +PyDoc_STRVAR(py_matrix_load_identity_doc, ".. function:: load_identity()\n" "\n" " Empty stack and set to identity.\n"); -static PyObject *bpygpu_matrix_load_identity(PyObject *UNUSED(self)) +static PyObject *py_matrix_load_identity(PyObject *UNUSED(self)) { GPU_matrix_identity_set(); Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_matrix_load_matrix_doc, +PyDoc_STRVAR(py_matrix_load_matrix_doc, ".. function:: load_matrix(matrix)\n" "\n" " Load a matrix into the stack.\n" "\n" " :param matrix: A 4x4 matrix.\n" " :type matrix: :class:`mathutils.Matrix`\n"); -static PyObject *bpygpu_matrix_load_matrix(PyObject *UNUSED(self), PyObject *value) +static PyObject *py_matrix_load_matrix(PyObject *UNUSED(self), PyObject *value) { MatrixObject *pymat; if (!Matrix_Parse4x4(value, &pymat)) { @@ -399,14 +399,14 @@ static PyObject *bpygpu_matrix_load_matrix(PyObject *UNUSED(self), PyObject *val Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_matrix_load_projection_matrix_doc, +PyDoc_STRVAR(py_matrix_load_projection_matrix_doc, ".. function:: load_projection_matrix(matrix)\n" "\n" " Load a projection matrix into the stack.\n" "\n" " :param matrix: A 4x4 matrix.\n" " :type matrix: :class:`mathutils.Matrix`\n"); -static PyObject *bpygpu_matrix_load_projection_matrix(PyObject *UNUSED(self), PyObject *value) +static PyObject *py_matrix_load_projection_matrix(PyObject *UNUSED(self), PyObject *value) { MatrixObject *pymat; if (!Matrix_Parse4x4(value, &pymat)) { @@ -422,42 +422,42 @@ static PyObject *bpygpu_matrix_load_projection_matrix(PyObject *UNUSED(self), Py /** \name Read State * \{ */ -PyDoc_STRVAR(bpygpu_matrix_get_projection_matrix_doc, +PyDoc_STRVAR(py_matrix_get_projection_matrix_doc, ".. function:: get_projection_matrix()\n" "\n" " Return a copy of the projection matrix.\n" "\n" " :return: A 4x4 projection matrix.\n" " :rtype: :class:`mathutils.Matrix`\n"); -static PyObject *bpygpu_matrix_get_projection_matrix(PyObject *UNUSED(self)) +static PyObject *py_matrix_get_projection_matrix(PyObject *UNUSED(self)) { float matrix[4][4]; GPU_matrix_projection_get(matrix); return Matrix_CreatePyObject(&matrix[0][0], 4, 4, NULL); } -PyDoc_STRVAR(bpygpu_matrix_get_model_view_matrix_doc, +PyDoc_STRVAR(py_matrix_get_model_view_matrix_doc, ".. function:: get_model_view_matrix()\n" "\n" " Return a copy of the model-view matrix.\n" "\n" " :return: A 4x4 view matrix.\n" " :rtype: :class:`mathutils.Matrix`\n"); -static PyObject *bpygpu_matrix_get_model_view_matrix(PyObject *UNUSED(self)) +static PyObject *py_matrix_get_model_view_matrix(PyObject *UNUSED(self)) { float matrix[4][4]; GPU_matrix_model_view_get(matrix); return Matrix_CreatePyObject(&matrix[0][0], 4, 4, NULL); } -PyDoc_STRVAR(bpygpu_matrix_get_normal_matrix_doc, +PyDoc_STRVAR(py_matrix_get_normal_matrix_doc, ".. function:: get_normal_matrix()\n" "\n" " Return a copy of the normal matrix.\n" "\n" " :return: A 3x3 normal matrix.\n" " :rtype: :class:`mathutils.Matrix`\n"); -static PyObject *bpygpu_matrix_get_normal_matrix(PyObject *UNUSED(self)) +static PyObject *py_matrix_get_normal_matrix(PyObject *UNUSED(self)) { float matrix[3][3]; GPU_matrix_normal_get(matrix); @@ -470,81 +470,78 @@ static PyObject *bpygpu_matrix_get_normal_matrix(PyObject *UNUSED(self)) /** \name Module * \{ */ -static struct PyMethodDef bpygpu_matrix_methods[] = { +static struct PyMethodDef py_matrix_methods[] = { /* Manage Stack */ - {"push", (PyCFunction)bpygpu_matrix_push, METH_NOARGS, bpygpu_matrix_push_doc}, - {"pop", (PyCFunction)bpygpu_matrix_pop, METH_NOARGS, bpygpu_matrix_pop_doc}, + {"push", (PyCFunction)py_matrix_push, METH_NOARGS, py_matrix_push_doc}, + {"pop", (PyCFunction)py_matrix_pop, METH_NOARGS, py_matrix_pop_doc}, {"push_projection", - (PyCFunction)bpygpu_matrix_push_projection, + (PyCFunction)py_matrix_push_projection, METH_NOARGS, - bpygpu_matrix_push_projection_doc}, + py_matrix_push_projection_doc}, {"pop_projection", - (PyCFunction)bpygpu_matrix_pop_projection, + (PyCFunction)py_matrix_pop_projection, METH_NOARGS, - bpygpu_matrix_pop_projection_doc}, + py_matrix_pop_projection_doc}, /* Stack (Context Manager) */ - {"push_pop", (PyCFunction)bpygpu_matrix_push_pop, METH_NOARGS, bpygpu_matrix_push_pop_doc}, + {"push_pop", (PyCFunction)py_matrix_push_pop, METH_NOARGS, py_matrix_push_pop_doc}, {"push_pop_projection", - (PyCFunction)bpygpu_matrix_push_pop_projection, + (PyCFunction)py_matrix_push_pop_projection, METH_NOARGS, - bpygpu_matrix_push_pop_projection_doc}, + py_matrix_push_pop_projection_doc}, /* Manipulate State */ {"multiply_matrix", - (PyCFunction)bpygpu_matrix_multiply_matrix, + (PyCFunction)py_matrix_multiply_matrix, METH_O, - bpygpu_matrix_multiply_matrix_doc}, - {"scale", (PyCFunction)bpygpu_matrix_scale, METH_O, bpygpu_matrix_scale_doc}, - {"scale_uniform", - (PyCFunction)bpygpu_matrix_scale_uniform, - METH_O, - bpygpu_matrix_scale_uniform_doc}, - {"translate", (PyCFunction)bpygpu_matrix_translate, METH_O, bpygpu_matrix_translate_doc}, + py_matrix_multiply_matrix_doc}, + {"scale", (PyCFunction)py_matrix_scale, METH_O, py_matrix_scale_doc}, + {"scale_uniform", (PyCFunction)py_matrix_scale_uniform, METH_O, py_matrix_scale_uniform_doc}, + {"translate", (PyCFunction)py_matrix_translate, METH_O, py_matrix_translate_doc}, /* TODO */ #if 0 - {"rotate", (PyCFunction)bpygpu_matrix_rotate, METH_O, bpygpu_matrix_rotate_doc}, - {"rotate_axis", (PyCFunction)bpygpu_matrix_rotate_axis, METH_O, bpygpu_matrix_rotate_axis_doc}, - {"look_at", (PyCFunction)bpygpu_matrix_look_at, METH_O, bpygpu_matrix_look_at_doc}, + {"rotate", (PyCFunction)py_matrix_rotate, METH_O, py_matrix_rotate_doc}, + {"rotate_axis", (PyCFunction)py_matrix_rotate_axis, METH_O, py_matrix_rotate_axis_doc}, + {"look_at", (PyCFunction)py_matrix_look_at, METH_O, py_matrix_look_at_doc}, #endif /* Write State */ - {"reset", (PyCFunction)bpygpu_matrix_reset, METH_NOARGS, bpygpu_matrix_reset_doc}, + {"reset", (PyCFunction)py_matrix_reset, METH_NOARGS, py_matrix_reset_doc}, {"load_identity", - (PyCFunction)bpygpu_matrix_load_identity, + (PyCFunction)py_matrix_load_identity, METH_NOARGS, - bpygpu_matrix_load_identity_doc}, - {"load_matrix", (PyCFunction)bpygpu_matrix_load_matrix, METH_O, bpygpu_matrix_load_matrix_doc}, + py_matrix_load_identity_doc}, + {"load_matrix", (PyCFunction)py_matrix_load_matrix, METH_O, py_matrix_load_matrix_doc}, {"load_projection_matrix", - (PyCFunction)bpygpu_matrix_load_projection_matrix, + (PyCFunction)py_matrix_load_projection_matrix, METH_O, - bpygpu_matrix_load_projection_matrix_doc}, + py_matrix_load_projection_matrix_doc}, /* Read State */ {"get_projection_matrix", - (PyCFunction)bpygpu_matrix_get_projection_matrix, + (PyCFunction)py_matrix_get_projection_matrix, METH_NOARGS, - bpygpu_matrix_get_projection_matrix_doc}, + py_matrix_get_projection_matrix_doc}, {"get_model_view_matrix", - (PyCFunction)bpygpu_matrix_get_model_view_matrix, + (PyCFunction)py_matrix_get_model_view_matrix, METH_NOARGS, - bpygpu_matrix_get_model_view_matrix_doc}, + py_matrix_get_model_view_matrix_doc}, {"get_normal_matrix", - (PyCFunction)bpygpu_matrix_get_normal_matrix, + (PyCFunction)py_matrix_get_normal_matrix, METH_NOARGS, - bpygpu_matrix_get_normal_matrix_doc}, + py_matrix_get_normal_matrix_doc}, {NULL, NULL, 0, NULL}, }; -PyDoc_STRVAR(bpygpu_matrix_doc, "This module provides access to the matrix stack."); +PyDoc_STRVAR(py_matrix_doc, "This module provides access to the matrix stack."); static PyModuleDef BPyGPU_matrix_module_def = { PyModuleDef_HEAD_INIT, .m_name = "gpu.matrix", - .m_doc = bpygpu_matrix_doc, - .m_methods = bpygpu_matrix_methods, + .m_doc = py_matrix_doc, + .m_methods = py_matrix_methods, }; PyObject *BPyInit_gpu_matrix(void) diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index 6d14dd0de5f..00d367c603b 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -58,9 +58,9 @@ /** \name GPUOffScreen Common Utilities * \{ */ -static int bpygpu_offscreen_valid_check(BPyGPUOffScreen *bpygpu_ofs) +static int py_offscreen_valid_check(BPyGPUOffScreen *py_ofs) { - if (UNLIKELY(bpygpu_ofs->ofs == NULL)) { + if (UNLIKELY(py_ofs->ofs == NULL)) { PyErr_SetString(PyExc_ReferenceError, "GPU offscreen was freed, no further access is valid"); return -1; } @@ -69,7 +69,7 @@ static int bpygpu_offscreen_valid_check(BPyGPUOffScreen *bpygpu_ofs) #define BPY_GPU_OFFSCREEN_CHECK_OBJ(bpygpu) \ { \ - if (UNLIKELY(bpygpu_offscreen_valid_check(bpygpu) == -1)) { \ + if (UNLIKELY(py_offscreen_valid_check(bpygpu) == -1)) { \ return NULL; \ } \ } \ @@ -81,7 +81,7 @@ static int bpygpu_offscreen_valid_check(BPyGPUOffScreen *bpygpu_ofs) /** \name GPUOffscreen Type * \{ */ -static PyObject *bpygpu_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args, PyObject *kwds) +static PyObject *py_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args, PyObject *kwds) { BPYGPU_IS_INIT_OR_ERROR_OBJ; @@ -112,23 +112,23 @@ static PyObject *bpygpu_offscreen_new(PyTypeObject *UNUSED(self), PyObject *args return BPyGPUOffScreen_CreatePyObject(ofs); } -PyDoc_STRVAR(bpygpu_offscreen_width_doc, "Width of the texture.\n\n:type: `int`"); -static PyObject *bpygpu_offscreen_width_get(BPyGPUOffScreen *self, void *UNUSED(type)) +PyDoc_STRVAR(py_offscreen_width_doc, "Width of the texture.\n\n:type: `int`"); +static PyObject *py_offscreen_width_get(BPyGPUOffScreen *self, void *UNUSED(type)) { BPY_GPU_OFFSCREEN_CHECK_OBJ(self); return PyLong_FromLong(GPU_offscreen_width(self->ofs)); } -PyDoc_STRVAR(bpygpu_offscreen_height_doc, "Height of the texture.\n\n:type: `int`"); -static PyObject *bpygpu_offscreen_height_get(BPyGPUOffScreen *self, void *UNUSED(type)) +PyDoc_STRVAR(py_offscreen_height_doc, "Height of the texture.\n\n:type: `int`"); +static PyObject *py_offscreen_height_get(BPyGPUOffScreen *self, void *UNUSED(type)) { BPY_GPU_OFFSCREEN_CHECK_OBJ(self); return PyLong_FromLong(GPU_offscreen_height(self->ofs)); } -PyDoc_STRVAR(bpygpu_offscreen_color_texture_doc, +PyDoc_STRVAR(py_offscreen_color_texture_doc, "OpenGL bindcode for the color texture.\n\n:type: `int`"); -static PyObject *bpygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void *UNUSED(type)) +static PyObject *py_offscreen_color_texture_get(BPyGPUOffScreen *self, void *UNUSED(type)) { BPY_GPU_OFFSCREEN_CHECK_OBJ(self); GPUTexture *texture = GPU_offscreen_color_texture(self->ofs); @@ -136,7 +136,7 @@ static PyObject *bpygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void } PyDoc_STRVAR( - bpygpu_offscreen_bind_doc, + py_offscreen_bind_doc, ".. method:: bind(save=True)\n" "\n" " Bind the offscreen object.\n" @@ -145,7 +145,7 @@ PyDoc_STRVAR( "\n" " :arg save: Save the current OpenGL state, so that it can be restored when unbinding.\n" " :type save: `bool`\n"); -static PyObject *bpygpu_offscreen_bind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds) +static PyObject *py_offscreen_bind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds) { BPY_GPU_OFFSCREEN_CHECK_OBJ(self); bool save = true; @@ -165,7 +165,7 @@ static PyObject *bpygpu_offscreen_bind(BPyGPUOffScreen *self, PyObject *args, Py return (PyObject *)self; } -PyDoc_STRVAR(bpygpu_offscreen_unbind_doc, +PyDoc_STRVAR(py_offscreen_unbind_doc, ".. method:: unbind(restore=True)\n" "\n" " Unbind the offscreen object.\n" @@ -173,7 +173,7 @@ PyDoc_STRVAR(bpygpu_offscreen_unbind_doc, " :arg restore: Restore the OpenGL state, can only be used when the state has been " "saved before.\n" " :type restore: `bool`\n"); -static PyObject *bpygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds) +static PyObject *py_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds) { bool restore = true; @@ -191,7 +191,7 @@ static PyObject *bpygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, } PyDoc_STRVAR( - bpygpu_offscreen_draw_view3d_doc, + py_offscreen_draw_view3d_doc, ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n" "\n" " Draw the 3d viewport in the offscreen object.\n" @@ -208,9 +208,7 @@ PyDoc_STRVAR( " :type view_matrix: :class:`mathutils.Matrix`\n" " :arg projection_matrix: Projection Matrix (e.g. ``camera.calc_matrix_camera(...)``).\n" " :type projection_matrix: :class:`mathutils.Matrix`\n"); -static PyObject *bpygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, - PyObject *args, - PyObject *kwds) +static PyObject *py_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds) { MatrixObject *py_mat_view, *py_mat_projection; PyObject *py_scene, *py_view_layer, *py_region, *py_view3d; @@ -272,12 +270,12 @@ static PyObject *bpygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_offscreen_free_doc, +PyDoc_STRVAR(py_offscreen_free_doc, ".. method:: free()\n" "\n" " Free the offscreen object.\n" " The framebuffer, texture and render objects will no longer be accessible.\n"); -static PyObject *bpygpu_offscreen_free(BPyGPUOffScreen *self) +static PyObject *py_offscreen_free(BPyGPUOffScreen *self) { BPY_GPU_OFFSCREEN_CHECK_OBJ(self); @@ -286,12 +284,12 @@ static PyObject *bpygpu_offscreen_free(BPyGPUOffScreen *self) Py_RETURN_NONE; } -static PyObject *bpygpu_offscreen_bind_context_enter(BPyGPUOffScreen *UNUSED(self)) +static PyObject *py_offscreen_bind_context_enter(BPyGPUOffScreen *UNUSED(self)) { Py_RETURN_NONE; } -static PyObject *bpygpu_offscreen_bind_context_exit(BPyGPUOffScreen *self, PyObject *UNUSED(args)) +static PyObject *py_offscreen_bind_context_exit(BPyGPUOffScreen *self, PyObject *UNUSED(args)) { GPU_offscreen_unbind(self->ofs, self->is_saved); Py_RETURN_NONE; @@ -305,41 +303,34 @@ static void BPyGPUOffScreen__tp_dealloc(BPyGPUOffScreen *self) Py_TYPE(self)->tp_free((PyObject *)self); } -static PyGetSetDef bpygpu_offscreen_getseters[] = { +static PyGetSetDef py_offscreen_getseters[] = { {"color_texture", - (getter)bpygpu_offscreen_color_texture_get, + (getter)py_offscreen_color_texture_get, (setter)NULL, - bpygpu_offscreen_color_texture_doc, - NULL}, - {"width", (getter)bpygpu_offscreen_width_get, (setter)NULL, bpygpu_offscreen_width_doc, NULL}, - {"height", - (getter)bpygpu_offscreen_height_get, - (setter)NULL, - bpygpu_offscreen_height_doc, + py_offscreen_color_texture_doc, NULL}, + {"width", (getter)py_offscreen_width_get, (setter)NULL, py_offscreen_width_doc, NULL}, + {"height", (getter)py_offscreen_height_get, (setter)NULL, py_offscreen_height_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -static struct PyMethodDef bpygpu_offscreen_methods[] = { - {"bind", - (PyCFunction)bpygpu_offscreen_bind, - METH_VARARGS | METH_KEYWORDS, - bpygpu_offscreen_bind_doc}, +static struct PyMethodDef py_offscreen_methods[] = { + {"bind", (PyCFunction)py_offscreen_bind, METH_VARARGS | METH_KEYWORDS, py_offscreen_bind_doc}, {"unbind", - (PyCFunction)bpygpu_offscreen_unbind, + (PyCFunction)py_offscreen_unbind, METH_VARARGS | METH_KEYWORDS, - bpygpu_offscreen_unbind_doc}, + py_offscreen_unbind_doc}, {"draw_view3d", - (PyCFunction)bpygpu_offscreen_draw_view3d, + (PyCFunction)py_offscreen_draw_view3d, METH_VARARGS | METH_KEYWORDS, - bpygpu_offscreen_draw_view3d_doc}, - {"free", (PyCFunction)bpygpu_offscreen_free, METH_NOARGS, bpygpu_offscreen_free_doc}, - {"__enter__", (PyCFunction)bpygpu_offscreen_bind_context_enter, METH_NOARGS}, - {"__exit__", (PyCFunction)bpygpu_offscreen_bind_context_exit, METH_VARARGS}, + py_offscreen_draw_view3d_doc}, + {"free", (PyCFunction)py_offscreen_free, METH_NOARGS, py_offscreen_free_doc}, + {"__enter__", (PyCFunction)py_offscreen_bind_context_enter, METH_NOARGS}, + {"__exit__", (PyCFunction)py_offscreen_bind_context_exit, METH_VARARGS}, {NULL, NULL, 0, NULL}, }; -PyDoc_STRVAR(bpygpu_offscreen_doc, +PyDoc_STRVAR(py_offscreen_doc, ".. class:: GPUOffScreen(width, height)\n" "\n" " This object gives access to off screen buffers.\n" @@ -353,10 +344,10 @@ PyTypeObject BPyGPUOffScreen_Type = { .tp_basicsize = sizeof(BPyGPUOffScreen), .tp_dealloc = (destructor)BPyGPUOffScreen__tp_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = bpygpu_offscreen_doc, - .tp_methods = bpygpu_offscreen_methods, - .tp_getset = bpygpu_offscreen_getseters, - .tp_new = bpygpu_offscreen_new, + .tp_doc = py_offscreen_doc, + .tp_methods = py_offscreen_methods, + .tp_getset = py_offscreen_getseters, + .tp_new = py_offscreen_new, }; /** \} */ diff --git a/source/blender/python/gpu/gpu_py_select.c b/source/blender/python/gpu/gpu_py_select.c index abe46301abb..4fa9d5ebc7a 100644 --- a/source/blender/python/gpu/gpu_py_select.c +++ b/source/blender/python/gpu/gpu_py_select.c @@ -40,14 +40,14 @@ /** \name Methods * \{ */ -PyDoc_STRVAR(bpygpu_select_load_id_doc, +PyDoc_STRVAR(py_select_load_id_doc, ".. function:: load_id(id)\n" "\n" " Set the selection ID.\n" "\n" " :param id: Number (32-bit uint).\n" " :type select: int\n"); -static PyObject *bpygpu_select_load_id(PyObject *UNUSED(self), PyObject *value) +static PyObject *py_select_load_id(PyObject *UNUSED(self), PyObject *value) { uint id; if ((id = PyC_Long_AsU32(value)) == (uint)-1) { @@ -62,18 +62,18 @@ static PyObject *bpygpu_select_load_id(PyObject *UNUSED(self), PyObject *value) /** \name Module * \{ */ -static struct PyMethodDef bpygpu_select_methods[] = { +static struct PyMethodDef py_select_methods[] = { /* Manage Stack */ - {"load_id", (PyCFunction)bpygpu_select_load_id, METH_O, bpygpu_select_load_id_doc}, + {"load_id", (PyCFunction)py_select_load_id, METH_O, py_select_load_id_doc}, {NULL, NULL, 0, NULL}, }; -PyDoc_STRVAR(bpygpu_select_doc, "This module provides access to selection."); +PyDoc_STRVAR(py_select_doc, "This module provides access to selection."); static PyModuleDef BPyGPU_select_module_def = { PyModuleDef_HEAD_INIT, .m_name = "gpu.select", - .m_doc = bpygpu_select_doc, - .m_methods = bpygpu_select_methods, + .m_doc = py_select_doc, + .m_methods = py_select_methods, }; PyObject *BPyInit_gpu_select(void) diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index c90a4a7bc9d..526b96f8584 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -39,45 +39,19 @@ /** \name Enum Conversion. * \{ */ -static int bpygpu_ParseBultinShaderEnum(PyObject *o, void *p) -{ - Py_ssize_t mode_id_len; - const char *mode_id = _PyUnicode_AsStringAndSize(o, &mode_id_len); - if (mode_id == NULL) { - PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name); - return 0; - } -#define MATCH_ID(id) \ - if (mode_id_len == (Py_ssize_t)strlen(STRINGIFY(id))) { \ - if (STREQ(mode_id, STRINGIFY(id))) { \ - mode = GPU_SHADER_##id; \ - goto success; \ - } \ - } \ - ((void)0) - - eGPUBuiltinShader mode; - MATCH_ID(2D_UNIFORM_COLOR); - MATCH_ID(2D_FLAT_COLOR); - MATCH_ID(2D_SMOOTH_COLOR); - MATCH_ID(2D_IMAGE); - MATCH_ID(3D_UNIFORM_COLOR); - MATCH_ID(3D_FLAT_COLOR); - MATCH_ID(3D_SMOOTH_COLOR); - MATCH_ID(3D_POLYLINE_UNIFORM_COLOR); - -#undef MATCH_ID - PyErr_Format(PyExc_ValueError, "unknown type literal: '%s'", mode_id); - return 0; - -success: - (*(eGPUBuiltinShader *)p) = mode; - return 1; -} +static const struct PyC_StringEnumItems pygpu_bultinshader_items[] = { + {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"}, + {GPU_SHADER_2D_FLAT_COLOR, "2D_FLAT_COLOR"}, + {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"}, + {GPU_SHADER_2D_IMAGE, "2D_IMAGE"}, + {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"}, + {GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"}, + {GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"}, + {GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "3D_POLYLINE_UNIFORM_COLOR"}, + {0, NULL}, +}; -static int bpygpu_uniform_location_get(GPUShader *shader, - const char *name, - const char *error_prefix) +static int py_uniform_location_get(GPUShader *shader, const char *name, const char *error_prefix) { const int uniform = GPU_shader_get_uniform(shader, name); @@ -94,7 +68,7 @@ static int bpygpu_uniform_location_get(GPUShader *shader, /** \name Shader Type * \{ */ -static PyObject *bpygpu_shader_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) +static PyObject *py_shader_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { BPYGPU_IS_INIT_OR_ERROR_OBJ; @@ -133,17 +107,17 @@ static PyObject *bpygpu_shader_new(PyTypeObject *UNUSED(type), PyObject *args, P } PyDoc_STRVAR( - bpygpu_shader_bind_doc, + py_shader_bind_doc, ".. method:: bind()\n" "\n" " Bind the shader object. Required to be able to change uniforms of this shader.\n"); -static PyObject *bpygpu_shader_bind(BPyGPUShader *self) +static PyObject *py_shader_bind(BPyGPUShader *self) { GPU_shader_bind(self->shader); Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_shader_uniform_from_name_doc, +PyDoc_STRVAR(py_shader_uniform_from_name_doc, ".. method:: uniform_from_name(name)\n" "\n" " Get uniform location by name.\n" @@ -152,14 +126,14 @@ PyDoc_STRVAR(bpygpu_shader_uniform_from_name_doc, " :type name: `str`\n" " :return: Location of the uniform variable.\n" " :rtype: `int`\n"); -static PyObject *bpygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *arg) +static PyObject *py_shader_uniform_from_name(BPyGPUShader *self, PyObject *arg) { const char *name = PyUnicode_AsUTF8(arg); if (name == NULL) { return NULL; } - const int uniform = bpygpu_uniform_location_get(self->shader, name, "GPUShader.get_uniform"); + const int uniform = py_uniform_location_get(self->shader, name, "GPUShader.get_uniform"); if (uniform == -1) { return NULL; @@ -169,7 +143,7 @@ static PyObject *bpygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *a } PyDoc_STRVAR( - bpygpu_shader_uniform_block_from_name_doc, + py_shader_uniform_block_from_name_doc, ".. method:: uniform_block_from_name(name)\n" "\n" " Get uniform block location by name.\n" @@ -178,7 +152,7 @@ PyDoc_STRVAR( " :type name: `str`\n" " :return: The location of the uniform block variable.\n" " :rtype: `int`\n"); -static PyObject *bpygpu_shader_uniform_block_from_name(BPyGPUShader *self, PyObject *arg) +static PyObject *py_shader_uniform_block_from_name(BPyGPUShader *self, PyObject *arg) { const char *name = PyUnicode_AsUTF8(arg); if (name == NULL) { @@ -195,12 +169,12 @@ static PyObject *bpygpu_shader_uniform_block_from_name(BPyGPUShader *self, PyObj return PyLong_FromLong(uniform); } -static bool bpygpu_shader_uniform_vector_imp(PyObject *args, - int elem_size, - int *r_location, - int *r_length, - int *r_count, - Py_buffer *r_pybuffer) +static bool py_shader_uniform_vector_imp(PyObject *args, + int elem_size, + int *r_location, + int *r_length, + int *r_count, + Py_buffer *r_pybuffer) { PyObject *buffer; @@ -223,7 +197,7 @@ static bool bpygpu_shader_uniform_vector_imp(PyObject *args, return true; } -PyDoc_STRVAR(bpygpu_shader_uniform_vector_float_doc, +PyDoc_STRVAR(py_shader_uniform_vector_float_doc, ".. method:: uniform_vector_float(location, buffer, length, count)\n" "\n" " Set the buffer to fill the uniform.\n" @@ -243,14 +217,13 @@ PyDoc_STRVAR(bpygpu_shader_uniform_vector_float_doc, " :param count: Specifies the number of elements, vector or matrices that are to " "be modified.\n" " :type count: int\n"); -static PyObject *bpygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject *args) +static PyObject *py_shader_uniform_vector_float(BPyGPUShader *self, PyObject *args) { int location, length, count; Py_buffer pybuffer; - if (!bpygpu_shader_uniform_vector_imp( - args, sizeof(float), &location, &length, &count, &pybuffer)) { + if (!py_shader_uniform_vector_imp(args, sizeof(float), &location, &length, &count, &pybuffer)) { return NULL; } @@ -261,18 +234,17 @@ static PyObject *bpygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_shader_uniform_vector_int_doc, +PyDoc_STRVAR(py_shader_uniform_vector_int_doc, ".. method:: uniform_vector_int(location, buffer, length, count)\n" "\n" " See GPUShader.uniform_vector_float(...) description.\n"); -static PyObject *bpygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject *args) +static PyObject *py_shader_uniform_vector_int(BPyGPUShader *self, PyObject *args) { int location, length, count; Py_buffer pybuffer; - if (!bpygpu_shader_uniform_vector_imp( - args, sizeof(int), &location, &length, &count, &pybuffer)) { + if (!py_shader_uniform_vector_imp(args, sizeof(int), &location, &length, &count, &pybuffer)) { return NULL; } @@ -283,7 +255,7 @@ static PyObject *bpygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject * Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_shader_uniform_bool_doc, +PyDoc_STRVAR(py_shader_uniform_bool_doc, ".. method:: uniform_bool(name, seq)\n" "\n" " Specify the value of a uniform variable for the current program object.\n" @@ -292,7 +264,7 @@ PyDoc_STRVAR(bpygpu_shader_uniform_bool_doc, " :type name: str\n" " :param seq: Value that will be used to update the specified uniform variable.\n" " :type seq: sequence of bools\n"); -static PyObject *bpygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args) +static PyObject *py_shader_uniform_bool(BPyGPUShader *self, PyObject *args) { const char *error_prefix = "GPUShader.uniform_bool"; @@ -336,7 +308,7 @@ static PyObject *bpygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args) return NULL; } - const int location = bpygpu_uniform_location_get(self->shader, params.id, error_prefix); + const int location = py_uniform_location_get(self->shader, params.id, error_prefix); if (location == -1) { return NULL; @@ -347,7 +319,7 @@ static PyObject *bpygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_shader_uniform_float_doc, +PyDoc_STRVAR(py_shader_uniform_float_doc, ".. method:: uniform_float(name, value)\n" "\n" " Specify the value of a uniform variable for the current program object.\n" @@ -356,7 +328,7 @@ PyDoc_STRVAR(bpygpu_shader_uniform_float_doc, " :type name: str\n" " :param value: Value that will be used to update the specified uniform variable.\n" " :type value: single number or sequence of numbers\n"); -static PyObject *bpygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args) +static PyObject *py_shader_uniform_float(BPyGPUShader *self, PyObject *args) { const char *error_prefix = "GPUShader.uniform_float"; @@ -405,7 +377,7 @@ static PyObject *bpygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args) return NULL; } - const int location = bpygpu_uniform_location_get(self->shader, params.id, error_prefix); + const int location = py_uniform_location_get(self->shader, params.id, error_prefix); if (location == -1) { return NULL; @@ -416,7 +388,7 @@ static PyObject *bpygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_shader_uniform_int_doc, +PyDoc_STRVAR(py_shader_uniform_int_doc, ".. method:: uniform_int(name, seq)\n" "\n" " Specify the value of a uniform variable for the current program object.\n" @@ -425,7 +397,7 @@ PyDoc_STRVAR(bpygpu_shader_uniform_int_doc, " :type name: str\n" " :param seq: Value that will be used to update the specified uniform variable.\n" " :type seq: sequence of numbers\n"); -static PyObject *bpygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args) +static PyObject *py_shader_uniform_int(BPyGPUShader *self, PyObject *args) { const char *error_prefix = "GPUShader.uniform_int"; @@ -475,7 +447,7 @@ static PyObject *bpygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args) return NULL; } - const int location = bpygpu_uniform_location_get(self->shader, params.id, error_prefix); + const int location = py_uniform_location_get(self->shader, params.id, error_prefix); if (location == -1) { return NULL; @@ -487,7 +459,7 @@ static PyObject *bpygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args) } PyDoc_STRVAR( - bpygpu_shader_attr_from_name_doc, + py_shader_attr_from_name_doc, ".. method:: attr_from_name(name)\n" "\n" " Get attribute location by name.\n" @@ -496,7 +468,7 @@ PyDoc_STRVAR( " :type name: str\n" " :return: The location of an attribute variable.\n" " :rtype: int\n"); -static PyObject *bpygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg) +static PyObject *py_shader_attr_from_name(BPyGPUShader *self, PyObject *arg) { const char *name = PyUnicode_AsUTF8(arg); if (name == NULL) { @@ -513,75 +485,69 @@ static PyObject *bpygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg) return PyLong_FromLong(attr); } -PyDoc_STRVAR(bpygpu_shader_calc_format_doc, +PyDoc_STRVAR(py_shader_calc_format_doc, ".. method:: calc_format()\n" "\n" " Build a new format based on the attributes of the shader.\n" "\n" " :return: vertex attribute format for the shader\n" " :rtype: GPUVertFormat\n"); -static PyObject *bpygpu_shader_calc_format(BPyGPUShader *self, PyObject *UNUSED(arg)) +static PyObject *py_shader_calc_format(BPyGPUShader *self, PyObject *UNUSED(arg)) { BPyGPUVertFormat *ret = (BPyGPUVertFormat *)BPyGPUVertFormat_CreatePyObject(NULL); GPU_vertformat_from_shader(&ret->fmt, self->shader); return (PyObject *)ret; } -static struct PyMethodDef bpygpu_shader_methods[] = { - {"bind", (PyCFunction)bpygpu_shader_bind, METH_NOARGS, bpygpu_shader_bind_doc}, +static struct PyMethodDef py_shader_methods[] = { + {"bind", (PyCFunction)py_shader_bind, METH_NOARGS, py_shader_bind_doc}, {"uniform_from_name", - (PyCFunction)bpygpu_shader_uniform_from_name, + (PyCFunction)py_shader_uniform_from_name, METH_O, - bpygpu_shader_uniform_from_name_doc}, + py_shader_uniform_from_name_doc}, {"uniform_block_from_name", - (PyCFunction)bpygpu_shader_uniform_block_from_name, + (PyCFunction)py_shader_uniform_block_from_name, METH_O, - bpygpu_shader_uniform_block_from_name_doc}, + py_shader_uniform_block_from_name_doc}, {"uniform_vector_float", - (PyCFunction)bpygpu_shader_uniform_vector_float, + (PyCFunction)py_shader_uniform_vector_float, METH_VARARGS, - bpygpu_shader_uniform_vector_float_doc}, + py_shader_uniform_vector_float_doc}, {"uniform_vector_int", - (PyCFunction)bpygpu_shader_uniform_vector_int, + (PyCFunction)py_shader_uniform_vector_int, METH_VARARGS, - bpygpu_shader_uniform_vector_int_doc}, + py_shader_uniform_vector_int_doc}, {"uniform_bool", - (PyCFunction)bpygpu_shader_uniform_bool, + (PyCFunction)py_shader_uniform_bool, METH_VARARGS, - bpygpu_shader_uniform_bool_doc}, + py_shader_uniform_bool_doc}, {"uniform_float", - (PyCFunction)bpygpu_shader_uniform_float, - METH_VARARGS, - bpygpu_shader_uniform_float_doc}, - {"uniform_int", - (PyCFunction)bpygpu_shader_uniform_int, + (PyCFunction)py_shader_uniform_float, METH_VARARGS, - bpygpu_shader_uniform_int_doc}, + py_shader_uniform_float_doc}, + {"uniform_int", (PyCFunction)py_shader_uniform_int, METH_VARARGS, py_shader_uniform_int_doc}, {"attr_from_name", - (PyCFunction)bpygpu_shader_attr_from_name, + (PyCFunction)py_shader_attr_from_name, METH_O, - bpygpu_shader_attr_from_name_doc}, - {"format_calc", - (PyCFunction)bpygpu_shader_calc_format, - METH_NOARGS, - bpygpu_shader_calc_format_doc}, + py_shader_attr_from_name_doc}, + {"format_calc", (PyCFunction)py_shader_calc_format, METH_NOARGS, py_shader_calc_format_doc}, {NULL, NULL, 0, NULL}, }; PyDoc_STRVAR( - bpygpu_shader_program_doc, + py_shader_program_doc, "The name of the program object for use by the OpenGL API (read-only).\n\n:type: int"); -static PyObject *bpygpu_shader_program_get(BPyGPUShader *self, void *UNUSED(closure)) +static PyObject *py_shader_program_get(BPyGPUShader *self, void *UNUSED(closure)) { return PyLong_FromLong(GPU_shader_get_program(self->shader)); } -static PyGetSetDef bpygpu_shader_getseters[] = { - {"program", (getter)bpygpu_shader_program_get, (setter)NULL, bpygpu_shader_program_doc, NULL}, +static PyGetSetDef py_shader_getseters[] = { + {"program", (getter)py_shader_program_get, (setter)NULL, py_shader_program_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -static void bpygpu_shader_dealloc(BPyGPUShader *self) +static void py_shader_dealloc(BPyGPUShader *self) { if (self->is_builtin == false) { GPU_shader_free(self->shader); @@ -590,7 +556,7 @@ static void bpygpu_shader_dealloc(BPyGPUShader *self) } PyDoc_STRVAR( - bpygpu_shader_doc, + py_shader_doc, ".. class:: GPUShader(vertexcode, fragcode, geocode=None, libcode=None, defines=None)\n" "\n" " GPUShader combines multiple GLSL shaders into a program used for drawing.\n" @@ -624,12 +590,12 @@ PyDoc_STRVAR( PyTypeObject BPyGPUShader_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUShader", .tp_basicsize = sizeof(BPyGPUShader), - .tp_dealloc = (destructor)bpygpu_shader_dealloc, + .tp_dealloc = (destructor)py_shader_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = bpygpu_shader_doc, - .tp_methods = bpygpu_shader_methods, - .tp_getset = bpygpu_shader_getseters, - .tp_new = bpygpu_shader_new, + .tp_doc = py_shader_doc, + .tp_methods = py_shader_methods, + .tp_getset = py_shader_getseters, + .tp_new = py_shader_new, }; /** \} */ @@ -638,17 +604,17 @@ PyTypeObject BPyGPUShader_Type = { /** \name gpu.shader Module API * \{ */ -PyDoc_STRVAR(bpygpu_shader_unbind_doc, +PyDoc_STRVAR(py_shader_unbind_doc, ".. function:: unbind()\n" "\n" " Unbind the bound shader object.\n"); -static PyObject *bpygpu_shader_unbind(BPyGPUShader *UNUSED(self)) +static PyObject *py_shader_unbind(BPyGPUShader *UNUSED(self)) { GPU_shader_unbind(); Py_RETURN_NONE; } -PyDoc_STRVAR(bpygpu_shader_from_builtin_doc, +PyDoc_STRVAR(py_shader_from_builtin_doc, ".. function:: from_builtin(shader_name)\n" "\n" " Shaders that are embedded in the blender internal code.\n" @@ -668,22 +634,21 @@ PyDoc_STRVAR(bpygpu_shader_from_builtin_doc, " :type shader_name: str\n" " :return: Shader object corresponding to the given name.\n" " :rtype: :class:`bpy.types.GPUShader`\n"); -static PyObject *bpygpu_shader_from_builtin(PyObject *UNUSED(self), PyObject *arg) +static PyObject *py_shader_from_builtin(PyObject *UNUSED(self), PyObject *arg) { BPYGPU_IS_INIT_OR_ERROR_OBJ; - eGPUBuiltinShader shader_id; - - if (!bpygpu_ParseBultinShaderEnum(arg, &shader_id)) { + struct PyC_StringEnum pygpu_bultinshader = {pygpu_bultinshader_items}; + if (!PyC_ParseStringEnum(arg, &pygpu_bultinshader)) { return NULL; } - GPUShader *shader = GPU_shader_get_builtin_shader(shader_id); + GPUShader *shader = GPU_shader_get_builtin_shader(pygpu_bultinshader.value_found); return BPyGPUShader_CreatePyObject(shader, true); } -PyDoc_STRVAR(bpygpu_shader_code_from_builtin_doc, +PyDoc_STRVAR(py_shader_code_from_builtin_doc, ".. function:: code_from_builtin(shader_name)\n" "\n" " Exposes the internal shader code for query.\n" @@ -699,10 +664,8 @@ PyDoc_STRVAR(bpygpu_shader_code_from_builtin_doc, " :type shader_name: str\n" " :return: Vertex, fragment and geometry shader codes.\n" " :rtype: dict\n"); -static PyObject *bpygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyObject *arg) +static PyObject *py_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyObject *arg) { - eGPUBuiltinShader shader_id; - const char *vert; const char *frag; const char *geom; @@ -710,11 +673,13 @@ static PyObject *bpygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyO PyObject *item, *r_dict; - if (!bpygpu_ParseBultinShaderEnum(arg, &shader_id)) { + struct PyC_StringEnum pygpu_bultinshader = {pygpu_bultinshader_items}; + if (!PyC_ParseStringEnum(arg, &pygpu_bultinshader)) { return NULL; } - GPU_shader_get_builtin_shader_code(shader_id, &vert, &frag, &geom, &defines); + GPU_shader_get_builtin_shader_code( + pygpu_bultinshader.value_found, &vert, &frag, &geom, &defines); r_dict = PyDict_New(); @@ -735,20 +700,17 @@ static PyObject *bpygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyO return r_dict; } -static struct PyMethodDef bpygpu_shader_module_methods[] = { - {"unbind", (PyCFunction)bpygpu_shader_unbind, METH_NOARGS, bpygpu_shader_unbind_doc}, - {"from_builtin", - (PyCFunction)bpygpu_shader_from_builtin, - METH_O, - bpygpu_shader_from_builtin_doc}, +static struct PyMethodDef py_shader_module_methods[] = { + {"unbind", (PyCFunction)py_shader_unbind, METH_NOARGS, py_shader_unbind_doc}, + {"from_builtin", (PyCFunction)py_shader_from_builtin, METH_O, py_shader_from_builtin_doc}, {"code_from_builtin", - (PyCFunction)bpygpu_shader_code_from_builtin, + (PyCFunction)py_shader_code_from_builtin, METH_O, - bpygpu_shader_code_from_builtin_doc}, + py_shader_code_from_builtin_doc}, {NULL, NULL, 0, NULL}, }; -PyDoc_STRVAR(bpygpu_shader_module_doc, +PyDoc_STRVAR(py_shader_module_doc, "This module provides access to GPUShader internal functions.\n" "\n" ".. rubric:: Built-in shaders\n" @@ -780,8 +742,8 @@ PyDoc_STRVAR(bpygpu_shader_module_doc, static PyModuleDef BPyGPU_shader_module_def = { PyModuleDef_HEAD_INIT, .m_name = "gpu.shader", - .m_doc = bpygpu_shader_module_doc, - .m_methods = bpygpu_shader_module_methods, + .m_doc = py_shader_module_doc, + .m_methods = py_shader_module_methods, }; /** \} */ diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c index d8c6e67816a..8e19eac76d0 100644 --- a/source/blender/python/gpu/gpu_py_vertex_buffer.c +++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c @@ -116,10 +116,10 @@ static void fill_format_sequence(void *data_dst_void, #undef WARN_TYPE_LIMIT_PUSH #undef WARN_TYPE_LIMIT_POP -static bool bpygpu_vertbuf_fill_impl(GPUVertBuf *vbo, - uint data_id, - PyObject *seq, - const char *error_prefix) +static bool py_vertbuf_fill_impl(GPUVertBuf *vbo, + uint data_id, + PyObject *seq, + const char *error_prefix) { const char *exc_str_size_mismatch = "Expected a %s of size %d, got %u"; @@ -213,10 +213,7 @@ static bool bpygpu_vertbuf_fill_impl(GPUVertBuf *vbo, return ok; } -static int bpygpu_attr_fill(GPUVertBuf *buf, - int id, - PyObject *py_seq_data, - const char *error_prefix) +static int py_attr_fill(GPUVertBuf *buf, int id, PyObject *py_seq_data, const char *error_prefix) { if (id < 0 || id >= GPU_vertbuf_get_format(buf)->attr_len) { PyErr_Format(PyExc_ValueError, "Format id %d out of range", id); @@ -228,7 +225,7 @@ static int bpygpu_attr_fill(GPUVertBuf *buf, return 0; } - if (!bpygpu_vertbuf_fill_impl(buf, (uint)id, py_seq_data, error_prefix)) { + if (!py_vertbuf_fill_impl(buf, (uint)id, py_seq_data, error_prefix)) { return 0; } @@ -241,7 +238,7 @@ static int bpygpu_attr_fill(GPUVertBuf *buf, /** \name VertBuf Type * \{ */ -static PyObject *bpygpu_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) +static PyObject *py_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { struct { PyObject *py_fmt; @@ -263,7 +260,7 @@ static PyObject *bpygpu_VertBuf_new(PyTypeObject *UNUSED(type), PyObject *args, return BPyGPUVertBuf_CreatePyObject(vbo); } -PyDoc_STRVAR(bpygpu_VertBuf_attr_fill_doc, +PyDoc_STRVAR(py_VertBuf_attr_fill_doc, ".. method:: attr_fill(id, data)\n" "\n" " Insert data into the buffer for a single attribute.\n" @@ -272,7 +269,7 @@ PyDoc_STRVAR(bpygpu_VertBuf_attr_fill_doc, " :type id: int or str\n" " :param data: Sequence of data that should be stored in the buffer\n" " :type data: sequence of values or tuples\n"); -static PyObject *bpygpu_VertBuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds) +static PyObject *py_VertBuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds) { PyObject *data; PyObject *identifier; @@ -302,22 +299,22 @@ static PyObject *bpygpu_VertBuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, P return NULL; } - if (!bpygpu_attr_fill(self->buf, id, data, "GPUVertBuf.attr_fill")) { + if (!py_attr_fill(self->buf, id, data, "GPUVertBuf.attr_fill")) { return NULL; } Py_RETURN_NONE; } -static struct PyMethodDef bpygpu_VertBuf_methods[] = { +static struct PyMethodDef py_VertBuf_methods[] = { {"attr_fill", - (PyCFunction)bpygpu_VertBuf_attr_fill, + (PyCFunction)py_VertBuf_attr_fill, METH_VARARGS | METH_KEYWORDS, - bpygpu_VertBuf_attr_fill_doc}, + py_VertBuf_attr_fill_doc}, {NULL, NULL, 0, NULL}, }; -static void bpygpu_VertBuf_dealloc(BPyGPUVertBuf *self) +static void py_VertBuf_dealloc(BPyGPUVertBuf *self) { GPU_vertbuf_discard(self->buf); Py_TYPE(self)->tp_free(self); @@ -335,11 +332,11 @@ PyDoc_STRVAR(py_gpu_vertex_buffer_doc, PyTypeObject BPyGPUVertBuf_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUVertBuf", .tp_basicsize = sizeof(BPyGPUVertBuf), - .tp_dealloc = (destructor)bpygpu_VertBuf_dealloc, + .tp_dealloc = (destructor)py_VertBuf_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = py_gpu_vertex_buffer_doc, - .tp_methods = bpygpu_VertBuf_methods, - .tp_new = bpygpu_VertBuf_new, + .tp_methods = py_VertBuf_methods, + .tp_new = py_VertBuf_new, }; /** \} */ diff --git a/source/blender/python/gpu/gpu_py_vertex_format.c b/source/blender/python/gpu/gpu_py_vertex_format.c index 2d45eadcfe3..7d1e4ee4868 100644 --- a/source/blender/python/gpu/gpu_py_vertex_format.c +++ b/source/blender/python/gpu/gpu_py_vertex_format.c @@ -50,7 +50,7 @@ * Use with PyArg_ParseTuple's "O&" formatting. * \{ */ -static int bpygpu_parse_component_type(const char *str, int length) +static int py_parse_component_type(const char *str, int length) { if (length == 2) { switch (*((ushort *)str)) { @@ -83,7 +83,7 @@ static int bpygpu_parse_component_type(const char *str, int length) return -1; } -static int bpygpu_parse_fetch_mode(const char *str, int length) +static int py_parse_fetch_mode(const char *str, int length) { #define MATCH_ID(id) \ if (length == strlen(STRINGIFY(id))) { \ @@ -102,7 +102,7 @@ static int bpygpu_parse_fetch_mode(const char *str, int length) return -1; } -static int bpygpu_ParseVertCompType(PyObject *o, void *p) +static int py_ParseVertCompType(PyObject *o, void *p) { Py_ssize_t length; const char *str = _PyUnicode_AsStringAndSize(o, &length); @@ -112,7 +112,7 @@ static int bpygpu_ParseVertCompType(PyObject *o, void *p) return 0; } - const int comp_type = bpygpu_parse_component_type(str, length); + const int comp_type = py_parse_component_type(str, length); if (comp_type == -1) { PyErr_Format(PyExc_ValueError, "unknown component type: '%s", str); return 0; @@ -122,7 +122,7 @@ static int bpygpu_ParseVertCompType(PyObject *o, void *p) return 1; } -static int bpygpu_ParseVertFetchMode(PyObject *o, void *p) +static int py_ParseVertFetchMode(PyObject *o, void *p) { Py_ssize_t length; const char *str = _PyUnicode_AsStringAndSize(o, &length); @@ -132,7 +132,7 @@ static int bpygpu_ParseVertFetchMode(PyObject *o, void *p) return 0; } - const int fetch_mode = bpygpu_parse_fetch_mode(str, length); + const int fetch_mode = py_parse_fetch_mode(str, length); if (fetch_mode == -1) { PyErr_Format(PyExc_ValueError, "unknown type literal: '%s'", str); return 0; @@ -148,7 +148,7 @@ static int bpygpu_ParseVertFetchMode(PyObject *o, void *p) /** \name VertFormat Type * \{ */ -static PyObject *bpygpu_VertFormat_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) +static PyObject *py_VertFormat_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { if (PyTuple_GET_SIZE(args) || (kwds && PyDict_Size(kwds))) { PyErr_SetString(PyExc_ValueError, "This function takes no arguments"); @@ -158,7 +158,7 @@ static PyObject *bpygpu_VertFormat_new(PyTypeObject *UNUSED(type), PyObject *arg } PyDoc_STRVAR( - bpygpu_VertFormat_attr_add_doc, + py_VertFormat_attr_add_doc, ".. method:: attr_add(id, comp_type, len, fetch_mode)\n" "\n" " Add a new attribute to the format.\n" @@ -177,7 +177,7 @@ PyDoc_STRVAR( " converted to a normal 4 byte float when used.\n" " Possible values are `FLOAT`, `INT`, `INT_TO_FLOAT_UNIT` and `INT_TO_FLOAT`.\n" " :type fetch_mode: `str`\n"); -static PyObject *bpygpu_VertFormat_attr_add(BPyGPUVertFormat *self, PyObject *args, PyObject *kwds) +static PyObject *py_VertFormat_attr_add(BPyGPUVertFormat *self, PyObject *args, PyObject *kwds) { struct { const char *id; @@ -197,10 +197,10 @@ static PyObject *bpygpu_VertFormat_attr_add(BPyGPUVertFormat *self, PyObject *ar kwds, &_parser, ¶ms.id, - bpygpu_ParseVertCompType, + py_ParseVertCompType, ¶ms.comp_type, ¶ms.len, - bpygpu_ParseVertFetchMode, + py_ParseVertFetchMode, ¶ms.fetch_mode)) { return NULL; } @@ -210,31 +210,31 @@ static PyObject *bpygpu_VertFormat_attr_add(BPyGPUVertFormat *self, PyObject *ar return PyLong_FromLong(attr_id); } -static struct PyMethodDef bpygpu_VertFormat_methods[] = { +static struct PyMethodDef py_VertFormat_methods[] = { {"attr_add", - (PyCFunction)bpygpu_VertFormat_attr_add, + (PyCFunction)py_VertFormat_attr_add, METH_VARARGS | METH_KEYWORDS, - bpygpu_VertFormat_attr_add_doc}, + py_VertFormat_attr_add_doc}, {NULL, NULL, 0, NULL}, }; -static void bpygpu_VertFormat_dealloc(BPyGPUVertFormat *self) +static void py_VertFormat_dealloc(BPyGPUVertFormat *self) { Py_TYPE(self)->tp_free(self); } -PyDoc_STRVAR(bpygpu_VertFormat_doc, +PyDoc_STRVAR(py_VertFormat_doc, ".. class:: GPUVertFormat()\n" "\n" " This object contains information about the structure of a vertex buffer.\n"); PyTypeObject BPyGPUVertFormat_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUVertFormat", .tp_basicsize = sizeof(BPyGPUVertFormat), - .tp_dealloc = (destructor)bpygpu_VertFormat_dealloc, + .tp_dealloc = (destructor)py_VertFormat_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = bpygpu_VertFormat_doc, - .tp_methods = bpygpu_VertFormat_methods, - .tp_new = bpygpu_VertFormat_new, + .tp_doc = py_VertFormat_doc, + .tp_methods = py_VertFormat_methods, + .tp_new = py_VertFormat_new, }; /** \} */ diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index 9d69d91c8c8..354086ef4c3 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -43,6 +43,8 @@ #include "MEM_guardedalloc.h" +#include "DNA_ID.h" /* MAX_IDPROP_NAME */ + #include "../generic/py_capi_utils.h" /* initial definition of callback slots we'll probably have more than 1 */ diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt index d1f69ddfc02..0046474d064 100644 --- a/source/blender/render/CMakeLists.txt +++ b/source/blender/render/CMakeLists.txt @@ -60,9 +60,9 @@ set(SRC RE_texture.h intern/initrender.h + intern/pipeline.h intern/render_result.h intern/render_types.h - intern/pipeline.h intern/texture_common.h intern/zbuf.h ) diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index 4dd2b300700..3e73ac77fc6 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -114,7 +114,7 @@ typedef struct RenderResult { /* target image size */ int rectx, recty; - short crop, sample_nr; + short sample_nr; /* The following rect32, rectf and rectz buffers are for temporary storage only, * for RenderResult structs created in #RE_AcquireResultImage - which do not have RenderView */ diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 769077c0e8c..5685911c42e 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -288,7 +288,7 @@ RenderResult *RE_engine_begin_result( disprect.ymin = y; disprect.ymax = y + h; - result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname); + result = render_result_new(re, &disprect, RR_USE_MEM, layername, viewname); /* todo: make this thread safe */ @@ -846,7 +846,7 @@ int RE_engine_render(Render *re, int do_all) if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) { savebuffers = RR_USE_EXR; } - re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); } BLI_rw_mutex_unlock(&re->resultmutex); diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 3d19e5e6c15..6b55b82ac97 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -905,7 +905,7 @@ static void render_result_rescale(Render *re) if (src_rectf != NULL) { float *dst_rectf = NULL; - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, ""); + re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, ""); if (re->result != NULL) { dst_rectf = RE_RenderViewGetById(re->result, 0)->rectf; @@ -1162,7 +1162,7 @@ static void render_result_uncrop(Render *re) /* weak is: it chances disprect from border */ render_result_disprect_to_full_resolution(re); - rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + rres = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data); render_result_clone_passes(re, rres, NULL); @@ -1358,7 +1358,7 @@ static void do_render_composite(Render *re) if ((re->r.mode & R_CROP) == 0) { render_result_disprect_to_full_resolution(re); } - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); BLI_rw_mutex_unlock(&re->resultmutex); diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index dfce51ec3ab..1ed894751ce 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -285,12 +285,8 @@ RenderPass *render_layer_add_pass(RenderResult *rr, /* will read info from Render *re to define layers */ /* called in threads */ /* re->winx,winy is coordinate space of entire image, partrct the part within */ -RenderResult *render_result_new(Render *re, - rcti *partrct, - int crop, - int savebuffers, - const char *layername, - const char *viewname) +RenderResult *render_result_new( + Render *re, rcti *partrct, int savebuffers, const char *layername, const char *viewname) { RenderResult *rr; RenderLayer *rl; @@ -308,9 +304,7 @@ RenderResult *render_result_new(Render *re, rr->rectx = rectx; rr->recty = recty; rr->renrect.xmin = 0; - rr->renrect.xmax = rectx - 2 * crop; - /* crop is one or two extra pixels rendered for filtering, is used for merging and display too */ - rr->crop = crop; + rr->renrect.xmax = rectx; /* tilerect is relative coordinates within render disprect. do not subtract crop yet */ rr->tilerect.xmin = partrct->xmin - re->disprect.xmin; @@ -827,20 +821,8 @@ static void do_merge_tile( copylen = tilex = rrpart->rectx; tiley = rrpart->recty; - if (rrpart->crop) { /* filters add pixel extra */ - tile += pixsize * (rrpart->crop + ((size_t)rrpart->crop) * tilex); - - copylen = tilex - 2 * rrpart->crop; - tiley -= 2 * rrpart->crop; - - ofs = (((size_t)rrpart->tilerect.ymin) + rrpart->crop) * rr->rectx + - (rrpart->tilerect.xmin + rrpart->crop); - target += pixsize * ofs; - } - else { - ofs = (((size_t)rrpart->tilerect.ymin) * rr->rectx + rrpart->tilerect.xmin); - target += pixsize * ofs; - } + ofs = (((size_t)rrpart->tilerect.ymin) * rr->rectx + rrpart->tilerect.xmin); + target += pixsize * ofs; copylen *= sizeof(float) * pixsize; tilex *= pixsize; @@ -1107,7 +1089,7 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons { RenderLayer *rlp, *rl; RenderPass *rpassp; - int offs, partx, party; + int partx, party; BLI_thread_lock(LOCK_IMAGE); @@ -1120,13 +1102,6 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons continue; } - if (rrpart->crop) { /* filters add pixel extra */ - offs = (rrpart->crop + rrpart->crop * rrpart->rectx); - } - else { - offs = 0; - } - /* passes are allocated in sync */ for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) { const int xstride = rpassp->channels; @@ -1141,13 +1116,13 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, cons fullname, xstride, xstride * rrpart->rectx, - rpassp->rect + a + xstride * offs); + rpassp->rect + a); } } } - party = rrpart->tilerect.ymin + rrpart->crop; - partx = rrpart->tilerect.xmin + rrpart->crop; + party = rrpart->tilerect.ymin; + partx = rrpart->tilerect.xmin; for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) { rl = RE_GetRenderLayer(rr, rlp->name); @@ -1267,7 +1242,7 @@ void render_result_exr_file_end(Render *re, RenderEngine *engine) /* Create new render result in memory instead of on disk. */ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); render_result_free_list(&re->fullresult, re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); BLI_rw_mutex_unlock(&re->resultmutex); LISTBASE_FOREACH (RenderLayer *, rl, &re->result->layers) { @@ -1429,7 +1404,7 @@ bool render_result_exr_file_cache_read(Render *re) char *root = U.render_cachedir; RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); /* First try cache. */ render_result_exr_file_cache_path(re->scene, root, str); diff --git a/source/blender/render/intern/render_result.h b/source/blender/render/intern/render_result.h index 5c487223e94..67edd075e24 100644 --- a/source/blender/render/intern/render_result.h +++ b/source/blender/render/intern/render_result.h @@ -51,7 +51,6 @@ extern "C" { struct RenderResult *render_result_new(struct Render *re, struct rcti *partrct, - int crop, int savebuffers, const char *layername, const char *viewname); diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c index e2568e0a013..4254f6b0aa6 100644 --- a/source/blender/render/intern/texture_pointdensity.c +++ b/source/blender/render/intern/texture_pointdensity.c @@ -44,6 +44,7 @@ #include "BKE_colorband.h" #include "BKE_colortools.h" +#include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_lattice.h" #include "BKE_object.h" diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt index 18755e7e6bc..db46fe91718 100644 --- a/source/blender/sequencer/CMakeLists.txt +++ b/source/blender/sequencer/CMakeLists.txt @@ -21,6 +21,7 @@ set(INC . intern + ../blenfont ../blenkernel ../blenlib ../blenloader @@ -30,7 +31,6 @@ set(INC ../makesdna ../makesrna ../render - ../blenfont ../windowmanager ../../../intern/atomic ../../../intern/guardedalloc diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index 11e213d842d..3a9c23de5cc 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -23,10 +23,15 @@ * \ingroup sequencer */ +#include "DNA_scene_types.h" + #ifdef __cplusplus extern "C" { #endif +struct BlendDataReader; +struct BlendLibReader; +struct BlendWriter; struct Depsgraph; struct Editing; struct GPUOffScreen; @@ -47,6 +52,7 @@ struct bSound; struct BlendWriter; struct BlendDataReader; struct BlendLibReader; +struct SequencerToolSettings; /* Wipe effect */ enum { @@ -161,7 +167,7 @@ void SEQ_render_new_render_data(struct Main *bmain, int preview_render_size, int for_render, SeqRenderData *r_context); -int SEQ_render_evaluate_frame(struct Scene *scene, int timeline_frame); +int SEQ_render_evaluate_frame(struct ListBase *seqbase, int timeline_frame); struct StripElem *SEQ_render_give_stripelem(struct Sequence *seq, int timeline_frame); /* ********************************************************************** @@ -179,9 +185,16 @@ void SEQ_render_pixel_from_sequencer_space_v4(struct Scene *scene, float pixel[4 * Sequencer scene functions * ********************************************************************** */ +struct SequencerToolSettings *SEQ_tool_settings_init(void); +void SEQ_tool_settings_free(struct SequencerToolSettings *tool_settings); +eSeqImageFitMethod SEQ_tool_settings_fit_method_get(struct Scene *scene); +void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fit_method); + +struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings); struct Editing *BKE_sequencer_editing_get(struct Scene *scene, bool alloc); struct Editing *BKE_sequencer_editing_ensure(struct Scene *scene); void BKE_sequencer_editing_free(struct Scene *scene, const bool do_id_user); +struct ListBase *SEQ_active_seqbase_get(const struct Editing *ed); void BKE_sequencer_sort(struct Scene *scene); struct Sequence *BKE_sequencer_from_elem(ListBase *seqbase, struct StripElem *se); struct Sequence *BKE_sequencer_active_get(struct Scene *scene); @@ -361,6 +374,20 @@ void BKE_sequence_invalidate_cache_in_range(struct Scene *scene, void BKE_sequencer_all_free_anim_ibufs(struct Scene *scene, int timeline_frame); /* ********************************************************************** + * util.c + * + * Add strips + * ********************************************************************** + */ + +void SEQ_set_scale_to_fit(const struct Sequence *seq, + const int image_width, + const int image_height, + const int preview_width, + const int preview_height, + const eSeqImageFitMethod fit_method); + +/* ********************************************************************** * sequencer.c * * Add strips @@ -376,6 +403,7 @@ typedef struct SeqLoadInfo { int type; int len; /* only for image strips */ char path[1024]; /* 1024 = FILE_MAX */ + eSeqImageFitMethod fit_method; /* multiview */ char views_format; @@ -595,6 +623,33 @@ struct Sequence *SEQ_edit_strip_split(struct Main *bmain, struct Sequence *seq, const int timeline_frame, const eSeqSplitMethod method); +bool SEQ_edit_remove_gaps(struct Scene *scene, + struct ListBase *seqbase, + const int initial_frame, + const bool remove_all_gaps); + +/* ********************************************************************** + * strip_time.c + * + * Editing functions + * ********************************************************************** + */ + +void SEQ_timeline_boundbox(const struct Scene *scene, + const struct ListBase *seqbase, + struct rctf *rect); + +/* ********************************************************************** + * strip_transform.c + * + * Editing functions + * ********************************************************************** + */ + +void SEQ_offset_after_frame(struct Scene *scene, + struct ListBase *seqbase, + const int delta, + const int timeline_frame); #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index ba16206ce97..8ffbc453517 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -42,6 +42,7 @@ #include "DNA_scene_types.h" #include "DNA_sequence_types.h" #include "DNA_space_types.h" +#include "DNA_vfont_types.h" #include "BKE_fcurve.h" #include "BKE_lib_id.h" diff --git a/source/blender/sequencer/intern/effects.h b/source/blender/sequencer/intern/effects.h index 58e0a97d4c5..6a94c0ea9d9 100644 --- a/source/blender/sequencer/intern/effects.h +++ b/source/blender/sequencer/intern/effects.h @@ -28,8 +28,8 @@ extern "C" { #endif struct Scene; -struct Sequence; struct SeqRenderData; +struct Sequence; /* ********************************************************************** * sequencer.c diff --git a/source/blender/sequencer/intern/image_cache.h b/source/blender/sequencer/intern/image_cache.h index 0fcf0548628..2cb35670a2c 100644 --- a/source/blender/sequencer/intern/image_cache.h +++ b/source/blender/sequencer/intern/image_cache.h @@ -30,8 +30,8 @@ extern "C" { struct ImBuf; struct Main; struct Scene; -struct Sequence; struct SeqRenderData; +struct Sequence; #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/multiview.h b/source/blender/sequencer/intern/multiview.h index e1f998d18e2..bbc66c6f84c 100644 --- a/source/blender/sequencer/intern/multiview.h +++ b/source/blender/sequencer/intern/multiview.h @@ -27,14 +27,7 @@ extern "C" { #endif -struct Editing; -struct ImBuf; -struct Main; -struct Mask; struct Scene; -struct Sequence; -struct StripColorBalance; -struct StripElem; /* ********************************************************************** * sequencer.c diff --git a/source/blender/sequencer/intern/prefetch.h b/source/blender/sequencer/intern/prefetch.h index aa47a9dbcfc..1633ee297f8 100644 --- a/source/blender/sequencer/intern/prefetch.h +++ b/source/blender/sequencer/intern/prefetch.h @@ -27,11 +27,9 @@ extern "C" { #endif -struct ImBuf; -struct Main; struct Scene; -struct Sequence; struct SeqRenderData; +struct Sequence; #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/proxy.h b/source/blender/sequencer/intern/proxy.h index a362a318a5a..a65fdcd42fe 100644 --- a/source/blender/sequencer/intern/proxy.h +++ b/source/blender/sequencer/intern/proxy.h @@ -27,10 +27,10 @@ extern "C" { #endif -struct anim; struct ImBuf; struct SeqRenderData; struct Sequence; +struct anim; #define PROXY_MAXFILE (2 * FILE_MAXDIR + FILE_MAXFILE) struct ImBuf *seq_proxy_fetch(const struct SeqRenderData *context, diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 008ea1cd3a0..2e757a06751 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -337,16 +337,17 @@ static int evaluate_seq_frame_gen(Sequence **seq_arr, return totseq; } -int SEQ_render_evaluate_frame(Scene *scene, int timeline_frame) +/** + * Count number of strips in timeline at timeline_frame + * + * \param seqbase: ListBase in which strips are located + * \param timeline_frame: frame on timeline from where gaps are searched for + * \return number of strips + */ +int SEQ_render_evaluate_frame(ListBase *seqbase, int timeline_frame) { - Editing *ed = BKE_sequencer_editing_get(scene, false); Sequence *seq_arr[MAXSEQ + 1]; - - if (ed == NULL) { - return 0; - } - - return evaluate_seq_frame_gen(seq_arr, ed->seqbasep, timeline_frame, 0); + return evaluate_seq_frame_gen(seq_arr, seqbase, timeline_frame, 0); } static bool video_seq_is_rendered(Sequence *seq) @@ -527,7 +528,6 @@ static void sequencer_image_transform_init(void *handle_v, handle->ibuf_source = init_data->ibuf_source; handle->ibuf_out = init_data->ibuf_out; handle->transform = init_data->transform; - handle->scale_to_fit = init_data->scale_to_fit; handle->image_scale_factor = init_data->image_scale_factor; handle->for_render = init_data->for_render; @@ -539,8 +539,8 @@ static void *sequencer_image_transform_do_thread(void *data_v) { const ImageTransformThreadData *data = (ImageTransformThreadData *)data_v; const StripTransform *transform = data->transform; - const float scale_x = transform->scale_x * data->scale_to_fit; - const float scale_y = transform->scale_y * data->scale_to_fit; + const float scale_x = transform->scale_x * data->image_scale_factor; + const float scale_y = transform->scale_y * data->image_scale_factor; const float scale_to_fit_offs_x = (data->ibuf_out->x - data->ibuf_source->x) / 2; const float scale_to_fit_offs_y = (data->ibuf_out->y - data->ibuf_source->y) / 2; const float translate_x = transform->xofs * data->image_scale_factor + scale_to_fit_offs_x; @@ -625,10 +625,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context, IMB_filtery(preprocessed_ibuf); } - /* Calculate scale factor, so image fits in preview area with original aspect ratio. */ - const float scale_to_fit_factor = MIN2((float)context->rectx / (float)ibuf->x, - (float)context->recty / (float)ibuf->y); - /* Get scale factor if preview resolution doesn't match project resolution. */ float preview_scale_factor; if (context->preview_render_size == SEQ_RENDER_SIZE_SCENE) { @@ -647,10 +643,10 @@ static ImBuf *input_preprocess(const SeqRenderData *context, const int height = ibuf->y; const StripCrop *c = seq->strip->crop; - const int left = c->left / scale_to_fit_factor * preview_scale_factor; - const int right = c->right / scale_to_fit_factor * preview_scale_factor; - const int top = c->top / scale_to_fit_factor * preview_scale_factor; - const int bottom = c->bottom / scale_to_fit_factor * preview_scale_factor; + const int left = c->left; + const int right = c->right; + const int top = c->top; + const int bottom = c->bottom; const float col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; /* Left. */ @@ -672,7 +668,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context, init_data.ibuf_source = ibuf; init_data.ibuf_out = preprocessed_ibuf; init_data.transform = seq->strip->transform; - init_data.scale_to_fit = scale_to_fit_factor; init_data.image_scale_factor = preview_scale_factor; init_data.for_render = context->for_render; IMB_processor_apply_threaded(context->recty, diff --git a/source/blender/sequencer/intern/render.h b/source/blender/sequencer/intern/render.h index d5affeb547b..46748415187 100644 --- a/source/blender/sequencer/intern/render.h +++ b/source/blender/sequencer/intern/render.h @@ -27,7 +27,6 @@ extern "C" { #endif -struct Editing; struct ImBuf; struct ListBase; struct Scene; diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index c998886626c..87b608ef141 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -301,6 +301,46 @@ static void seq_new_fix_links_recursive(Sequence *seq) } } } + +SequencerToolSettings *SEQ_tool_settings_init(void) +{ + SequencerToolSettings *tool_settings = MEM_callocN(sizeof(SequencerToolSettings), + "Sequencer tool settings"); + tool_settings->fit_method = SEQ_SCALE_TO_FIT; + return tool_settings; +} + +void SEQ_tool_settings_free(SequencerToolSettings *tool_settings) +{ + MEM_freeN(tool_settings); +} + +eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene) +{ + const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + return tool_settings->fit_method; +} + +void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_method) +{ + SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; + tool_settings->fit_method = fit_method; +} + +/** + * Get seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase + * + * \param ed: sequence editor data + * \return pointer to active seqbase. returns NULL if ed is NULL + */ +ListBase *SEQ_active_seqbase_get(const Editing *ed) +{ + if (ed == NULL) { + return NULL; + } + + return ed->seqbasep; +} /** \} */ /* -------------------------------------------------------------------- */ @@ -609,4 +649,11 @@ static void seq_free_animdata(Scene *scene, Sequence *seq) } #undef SEQ_RNAPATH_MAXSTR + +SequencerToolSettings *SEQ_tool_settings_copy(SequencerToolSettings *tool_settings) +{ + SequencerToolSettings *tool_settings_copy = MEM_dupallocN(tool_settings); + return tool_settings_copy; +} + /** \} */ diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index d2e4025bdfc..e56dcf888a7 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -118,6 +118,16 @@ Sequence *BKE_sequencer_add_image_strip(bContext *C, ListBase *seqbasep, SeqLoad seq->flag |= seq_load->flag & SEQ_USE_VIEWS; seq_load_apply(CTX_data_main(C), scene, seq, seq_load); + + char file_path[FILE_MAX]; + BLI_join_dirfile(file_path, sizeof(file_path), seq_load->path, seq_load->name); + BLI_path_abs(file_path, BKE_main_blendfile_path(CTX_data_main(C))); + ImBuf *ibuf = IMB_loadiffname(file_path, IB_rect, seq->strip->colorspace_settings.name); + if (ibuf != NULL) { + SEQ_set_scale_to_fit(seq, ibuf->x, ibuf->y, scene->r.xsch, scene->r.ysch, seq_load->fit_method); + IMB_freeImBuf(ibuf); + } + BKE_sequence_invalidate_cache_composite(scene, seq); return seq; @@ -275,6 +285,11 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad IMB_anim_load_metadata(anim_arr[0]); seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); + + const float width = IMB_anim_get_image_width(anim_arr[0]); + const float height = IMB_anim_get_image_height(anim_arr[0]); + SEQ_set_scale_to_fit(seq, width, height, scene->r.xsch, scene->r.ysch, seq_load->fit_method); + BLI_strncpy(seq->name + 2, "Movie", SEQ_NAME_MAXSTR - 2); BKE_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq); diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index 3137a471470..a29810cc9ee 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -38,6 +38,8 @@ #include "BKE_scene.h" #include "BKE_sound.h" +#include "strip_time.h" + #include "SEQ_sequencer.h" int BKE_sequence_swap(Sequence *seq_a, Sequence *seq_b, const char **error_str) @@ -319,3 +321,36 @@ Sequence *SEQ_edit_strip_split(Main *bmain, BKE_sequence_calc(scene, right_seq); return right_seq; } + +/** + * Find gap after initial_frame and move strips on right side to close the gap + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param initial_frame: frame on timeline from where gaps are searched for + * \param remove_all_gaps: remove all gaps instead of one gap + * \return true if gap is removed, otherwise false + */ +bool SEQ_edit_remove_gaps(Scene *scene, + ListBase *seqbase, + const int initial_frame, + const bool remove_all_gaps) +{ + GapInfo gap_info = {0}; + seq_time_gap_info_get(scene, seqbase, initial_frame, &gap_info); + + if (!gap_info.gap_exists) { + return false; + } + + if (remove_all_gaps) { + while (gap_info.gap_exists) { + SEQ_offset_after_frame(scene, seqbase, -gap_info.gap_length, gap_info.gap_start_frame); + seq_time_gap_info_get(scene, seqbase, initial_frame, &gap_info); + } + } + else { + SEQ_offset_after_frame(scene, seqbase, -gap_info.gap_length, gap_info.gap_start_frame); + } + return true; +} diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index a0ae6d6f16d..d9074b2a683 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -351,3 +351,85 @@ float BKE_sequence_get_fps(Scene *scene, Sequence *seq) } return 0.0f; } + +/** + * Define boundary rectangle of sequencer timeline and fill in rect data + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param rect: data structure describing rectangle, that will be filled in by this function + */ +void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect) +{ + rect->xmin = scene->r.sfra; + rect->xmax = scene->r.efra + 1; + rect->ymin = 0.0f; + rect->ymax = 8.0f; + + if (seqbase == NULL) { + return; + } + + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (rect->xmin > seq->startdisp - 1) { + rect->xmin = seq->startdisp - 1; + } + if (rect->xmax < seq->enddisp + 1) { + rect->xmax = seq->enddisp + 1; + } + if (rect->ymax < seq->machine + 2) { + rect->ymax = seq->machine + 2; + } + } +} + +/** + * Find first gap between strips after initial_frame and describe it by filling data of r_gap_info + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param initial_frame: frame on timeline from where gaps are searched for + * \param r_gap_info: data structure describing gap, that will be filled in by this function + */ +void seq_time_gap_info_get(const Scene *scene, + ListBase *seqbase, + const int initial_frame, + GapInfo *r_gap_info) +{ + rctf rectf; + /* Get first and last frame. */ + SEQ_timeline_boundbox(scene, seqbase, &rectf); + const int sfra = (int)rectf.xmin; + const int efra = (int)rectf.xmax; + int timeline_frame = initial_frame; + r_gap_info->gap_exists = false; + + if (SEQ_render_evaluate_frame(seqbase, initial_frame) == 0) { + /* Search backward for gap_start_frame. */ + for (; timeline_frame >= sfra; timeline_frame--) { + if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) { + break; + } + } + r_gap_info->gap_start_frame = timeline_frame + 1; + timeline_frame = initial_frame; + } + else { + /* Search forward for gap_start_frame. */ + for (; timeline_frame <= efra; timeline_frame++) { + if (SEQ_render_evaluate_frame(seqbase, timeline_frame) == 0) { + r_gap_info->gap_start_frame = timeline_frame; + break; + } + } + } + /* Search forward for gap_end_frame. */ + for (; timeline_frame <= efra; timeline_frame++) { + if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) { + const int gap_end_frame = timeline_frame; + r_gap_info->gap_length = gap_end_frame - r_gap_info->gap_start_frame; + r_gap_info->gap_exists = true; + break; + } + } +} diff --git a/source/blender/sequencer/intern/strip_time.h b/source/blender/sequencer/intern/strip_time.h index e4fb7f1d2ec..ca9a935bc96 100644 --- a/source/blender/sequencer/intern/strip_time.h +++ b/source/blender/sequencer/intern/strip_time.h @@ -27,12 +27,24 @@ extern "C" { #endif +struct ListBase; struct Scene; struct Sequence; float seq_give_frame_index(struct Sequence *seq, float timeline_frame); void seq_update_sound_bounds_recursive(struct Scene *scene, struct Sequence *metaseq); +/* Describes gap between strips in timeline. */ +typedef struct GapInfo { + int gap_start_frame; /* Start frame of the gap. */ + int gap_length; /* Length of the gap. */ + bool gap_exists; /* False if there are no gaps. */ +} GapInfo; +void seq_time_gap_info_get(const struct Scene *scene, + struct ListBase *seqbase, + const int initial_frame, + struct GapInfo *r_gap_info); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 233f8e5b22e..4aabe87bce1 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -397,3 +397,33 @@ bool BKE_sequence_base_shuffle_time(ListBase *seqbasep, return offset ? false : true; } + +/** + * Move strips and markers (if not locked) that start after timeline_frame by delta frames + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param delta: offset in frames to be applied + * \param timeline_frame: frame on timeline from where strips are moved + */ +void SEQ_offset_after_frame(Scene *scene, + ListBase *seqbase, + const int delta, + const int timeline_frame) +{ + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (seq->startdisp >= timeline_frame) { + BKE_sequence_translate(scene, seq, delta); + BKE_sequence_calc(scene, seq); + BKE_sequence_invalidate_cache_preprocessed(scene, seq); + } + } + + if (!scene->toolsettings->lock_markers) { + LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) { + if (marker->frame >= timeline_frame) { + marker->frame += delta; + } + } + } +} diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index 2b1d36a7709..ab0b65dba7f 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -36,6 +36,7 @@ #include "BLI_listbase.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_utildefines.h" #include "BKE_image.h" #include "BKE_main.h" @@ -547,3 +548,36 @@ bool sequencer_seq_generates_image(Sequence *seq) } return false; } + +void SEQ_set_scale_to_fit(const Sequence *seq, + const int image_width, + const int image_height, + const int preview_width, + const int preview_height, + const eSeqImageFitMethod fit_method) +{ + StripTransform *transform = seq->strip->transform; + + switch (fit_method) { + case SEQ_SCALE_TO_FIT: + transform->scale_x = transform->scale_y = MIN2((float)preview_width / (float)image_width, + (float)preview_height / (float)image_height); + + break; + case SEQ_SCALE_TO_FILL: + + transform->scale_x = transform->scale_y = MAX2((float)preview_width / (float)image_width, + (float)preview_height / (float)image_height); + break; + case SEQ_STRETCH_TO_FILL: + transform->scale_x = (float)preview_width / (float)image_width; + transform->scale_y = (float)preview_height / (float)image_height; + break; + case SEQ_USE_ORIGINAL_SIZE: + transform->scale_x = 1.0f; + transform->scale_y = 1.0f; + break; + } + + return; +} diff --git a/source/blender/sequencer/intern/utils.h b/source/blender/sequencer/intern/utils.h index fe6041ec5e8..f30ea753d37 100644 --- a/source/blender/sequencer/intern/utils.h +++ b/source/blender/sequencer/intern/utils.h @@ -28,7 +28,6 @@ extern "C" { #endif struct Scene; -struct anim; bool sequencer_seq_generates_image(struct Sequence *seq); void seq_open_anim_file(struct Scene *scene, struct Sequence *seq, bool openfile); diff --git a/source/blender/shader_fx/intern/FX_ui_common.c b/source/blender/shader_fx/intern/FX_ui_common.c index c1e3b2e21cd..9a86e1e96f5 100644 --- a/source/blender/shader_fx/intern/FX_ui_common.c +++ b/source/blender/shader_fx/intern/FX_ui_common.c @@ -124,6 +124,59 @@ PointerRNA *shaderfx_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ #define ERROR_LIBDATA_MESSAGE TIP_("External library data") +static void gpencil_shaderfx_ops_extra_draw(bContext *C, uiLayout *layout, void *fx_v) +{ + PointerRNA op_ptr; + uiLayout *row; + ShaderFxData *fx = (ShaderFxData *)fx_v; + + PointerRNA ptr; + Object *ob = ED_object_active_context(C); + RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, &ptr); + uiLayoutSetContextPointer(layout, "shaderfx", &ptr); + uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + + uiLayoutSetUnitsX(layout, 4.0f); + + /* Duplicate. */ + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Duplicate"), + ICON_DUPLICATE, + "OBJECT_OT_shaderfx_copy"); + + uiItemS(layout); + + /* Move to first. */ + row = uiLayoutColumn(layout, false); + uiItemFullO(row, + "OBJECT_OT_shaderfx_move_to_index", + IFACE_("Move to First"), + ICON_TRIA_UP, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); + RNA_int_set(&op_ptr, "index", 0); + if (!fx->prev) { + uiLayoutSetEnabled(row, false); + } + + /* Move to last. */ + row = uiLayoutColumn(layout, false); + uiItemFullO(row, + "OBJECT_OT_shaderfx_move_to_index", + IFACE_("Move to Last"), + ICON_TRIA_DOWN, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); + RNA_int_set(&op_ptr, "index", BLI_listbase_count(&ob->shader_fx) - 1); + if (!fx->next) { + uiLayoutSetEnabled(row, false); + } +} + static void shaderfx_panel_header(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; @@ -159,6 +212,9 @@ static void shaderfx_panel_header(const bContext *UNUSED(C), Panel *panel) uiItemR(row, ptr, "show_viewport", 0, "", ICON_NONE); uiItemR(row, ptr, "show_render", 0, "", ICON_NONE); + /* Extra operators. */ + uiItemMenuF(row, "", ICON_DOWNARROW_HLT, gpencil_shaderfx_ops_extra_draw, fx); + row = uiLayoutRow(row, false); uiLayoutSetEmboss(row, UI_EMBOSS_NONE); uiItemO(row, "", ICON_X, "OBJECT_OT_shaderfx_remove"); diff --git a/source/blender/simulation/SIM_mass_spring.h b/source/blender/simulation/SIM_mass_spring.h index 42d7c86b539..43de8b155cf 100644 --- a/source/blender/simulation/SIM_mass_spring.h +++ b/source/blender/simulation/SIM_mass_spring.h @@ -52,7 +52,7 @@ int SIM_cloth_solve(struct Depsgraph *depsgraph, struct ClothModifierData *clmd, struct ListBase *effectors); void SIM_cloth_solver_set_positions(struct ClothModifierData *clmd); -void SIM_cloth_solver_set_volume(ClothModifierData *clmd); +void SIM_cloth_solver_set_volume(struct ClothModifierData *clmd); #ifdef __cplusplus } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index da2115e12fb..1f205a71338 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -671,6 +671,7 @@ struct wmDrag *WM_event_start_drag( struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags); void WM_event_drag_image(struct wmDrag *, struct ImBuf *, float scale, int sx, int sy); void WM_drag_free(struct wmDrag *drag); +void WM_drag_data_free(int dragtype, void *poin); void WM_drag_free_list(struct ListBase *lb); struct wmDropBox *WM_dropbox_add( @@ -681,9 +682,12 @@ struct wmDropBox *WM_dropbox_add( ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); /* ID drag and drop */ -void WM_drag_add_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent); -struct ID *WM_drag_ID(const struct wmDrag *drag, short idcode); -struct ID *WM_drag_ID_from_event(const struct wmEvent *event, short idcode); +void WM_drag_add_local_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent); +struct ID *WM_drag_get_local_ID(const struct wmDrag *drag, short idcode); +struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short idcode); + +struct wmDragAsset *WM_drag_get_asset_data(const struct wmDrag *drag, int idcode); +struct ID *WM_drag_get_local_ID_or_import_from_asset(const struct wmDrag *drag, int idcode); /* Set OpenGL viewport and scissor */ void wmViewport(const struct rcti *winrct); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 7fa2851cbf3..a2cc246e21e 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -298,6 +298,8 @@ typedef struct wmNotifier { #define NC_LINESTYLE (23 << 24) #define NC_CAMERA (24 << 24) #define NC_LIGHTPROBE (25 << 24) +/* Changes to asset data in the current .blend. */ +#define NC_ASSET (26 << 24) /* data type, 256 entries is enough, it can overlap */ #define NOTE_DATA 0x00FF0000 @@ -408,20 +410,21 @@ typedef struct wmNotifier { #define ND_SPACE_IMAGE (4 << 16) #define ND_SPACE_FILE_PARAMS (5 << 16) #define ND_SPACE_FILE_LIST (6 << 16) -#define ND_SPACE_NODE (7 << 16) -#define ND_SPACE_OUTLINER (8 << 16) -#define ND_SPACE_VIEW3D (9 << 16) -#define ND_SPACE_PROPERTIES (10 << 16) -#define ND_SPACE_TEXT (11 << 16) -#define ND_SPACE_TIME (12 << 16) -#define ND_SPACE_GRAPH (13 << 16) -#define ND_SPACE_DOPESHEET (14 << 16) -#define ND_SPACE_NLA (15 << 16) -#define ND_SPACE_SEQUENCER (16 << 16) -#define ND_SPACE_NODE_VIEW (17 << 16) -#define ND_SPACE_CHANGED (18 << 16) /*sent to a new editor type after it's replaced an old one*/ -#define ND_SPACE_CLIP (19 << 16) -#define ND_SPACE_FILE_PREVIEW (20 << 16) +#define ND_SPACE_ASSET_PARAMS (7 << 16) +#define ND_SPACE_NODE (8 << 16) +#define ND_SPACE_OUTLINER (9 << 16) +#define ND_SPACE_VIEW3D (10 << 16) +#define ND_SPACE_PROPERTIES (11 << 16) +#define ND_SPACE_TEXT (12 << 16) +#define ND_SPACE_TIME (13 << 16) +#define ND_SPACE_GRAPH (14 << 16) +#define ND_SPACE_DOPESHEET (15 << 16) +#define ND_SPACE_NLA (16 << 16) +#define ND_SPACE_SEQUENCER (17 << 16) +#define ND_SPACE_NODE_VIEW (18 << 16) +#define ND_SPACE_CHANGED (19 << 16) /*sent to a new editor type after it's replaced an old one*/ +#define ND_SPACE_CLIP (20 << 16) +#define ND_SPACE_FILE_PREVIEW (21 << 16) /* subtype, 256 entries too */ #define NOTE_SUBTYPE 0x0000FF00 @@ -834,12 +837,13 @@ typedef void (*wmPaintCursorDraw)(struct bContext *C, int, int, void *customdata /* *************** Drag and drop *************** */ #define WM_DRAG_ID 0 -#define WM_DRAG_RNA 1 -#define WM_DRAG_PATH 2 -#define WM_DRAG_NAME 3 -#define WM_DRAG_VALUE 4 -#define WM_DRAG_COLOR 5 -#define WM_DRAG_DATASTACK 6 +#define WM_DRAG_ASSET 1 +#define WM_DRAG_RNA 2 +#define WM_DRAG_PATH 3 +#define WM_DRAG_NAME 4 +#define WM_DRAG_VALUE 5 +#define WM_DRAG_COLOR 6 +#define WM_DRAG_DATASTACK 7 typedef enum wmDragFlags { WM_DRAG_NOP = 0, @@ -854,6 +858,13 @@ typedef struct wmDragID { struct ID *from_parent; } wmDragID; +typedef struct wmDragAsset { + char name[64]; /* MAX_NAME */ + /* Always freed. */ + const char *path; + int id_type; +} wmDragAsset; + typedef struct wmDrag { struct wmDrag *next, *prev; diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 373360c7b92..fe2e2d92127 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -37,6 +37,7 @@ #include "BIF_glutil.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_idtype.h" #include "GPU_shader.h" @@ -146,16 +147,23 @@ wmDrag *WM_event_start_drag( drag->flags = flags; drag->icon = icon; drag->type = type; - if (type == WM_DRAG_PATH) { - BLI_strncpy(drag->path, poin, FILE_MAX); - } - else if (type == WM_DRAG_ID) { - if (poin) { - WM_drag_add_ID(drag, poin, NULL); - } - } - else { - drag->poin = poin; + switch (type) { + case WM_DRAG_PATH: + BLI_strncpy(drag->path, poin, FILE_MAX); + break; + case WM_DRAG_ID: + if (poin) { + WM_drag_add_local_ID(drag, poin, NULL); + } + break; + case WM_DRAG_ASSET: + /* Move ownership of poin to wmDrag. */ + drag->poin = poin; + drag->flags |= WM_DRAG_FREE_DATA; + break; + default: + drag->poin = poin; + break; } drag->value = value; @@ -170,12 +178,26 @@ void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy) drag->sy = sy; } -void WM_drag_free(wmDrag *drag) +void WM_drag_data_free(int dragtype, void *poin) { - if ((drag->flags & WM_DRAG_FREE_DATA) && drag->poin) { - MEM_freeN(drag->poin); + /* Don't require all the callers to have a NULL-check, just allow passing NULL. */ + if (!poin) { + return; } + /* Not too nice, could become a callback. */ + if (dragtype == WM_DRAG_ASSET) { + wmDragAsset *asset_drag = poin; + MEM_freeN((void *)asset_drag->path); + } + MEM_freeN(poin); +} + +void WM_drag_free(wmDrag *drag) +{ + if (drag->flags & WM_DRAG_FREE_DATA) { + WM_drag_data_free(drag->type, drag->poin); + } BLI_freelistN(&drag->ids); MEM_freeN(drag); } @@ -279,7 +301,7 @@ void wm_drags_check_ops(bContext *C, const wmEvent *event) /* ************** IDs ***************** */ -void WM_drag_add_ID(wmDrag *drag, ID *id, ID *from_parent) +void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent) { /* Don't drag the same ID twice. */ LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) { @@ -302,7 +324,7 @@ void WM_drag_add_ID(wmDrag *drag, ID *id, ID *from_parent) BLI_addtail(&drag->ids, drag_id); } -ID *WM_drag_ID(const wmDrag *drag, short idcode) +ID *WM_drag_get_local_ID(const wmDrag *drag, short idcode) { if (drag->type != WM_DRAG_ID) { return NULL; @@ -317,14 +339,54 @@ ID *WM_drag_ID(const wmDrag *drag, short idcode) return (idcode == 0 || GS(id->name) == idcode) ? id : NULL; } -ID *WM_drag_ID_from_event(const wmEvent *event, short idcode) +ID *WM_drag_get_local_ID_from_event(const wmEvent *event, short idcode) { if (event->custom != EVT_DATA_DRAGDROP) { return NULL; } ListBase *lb = event->customdata; - return WM_drag_ID(lb->first, idcode); + return WM_drag_get_local_ID(lb->first, idcode); +} + +wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) +{ + if (drag->type != WM_DRAG_ASSET) { + return NULL; + } + + wmDragAsset *asset_drag = drag->poin; + return (idcode == 0 || asset_drag->id_type == idcode) ? asset_drag : NULL; +} + +static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) +{ + /* Append only for now, wmDragAsset could have a `link` bool. */ + return WM_file_append_datablock( + G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name); +} + +/** + * When dragging a local ID, return that. Otherwise, if dragging an asset-handle, link or append + * that depending on what was chosen by the drag-box (currently append only in fact). + */ +ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) +{ + if (!ELEM(drag->type, WM_DRAG_ASSET, WM_DRAG_ID)) { + return NULL; + } + + if (drag->type == WM_DRAG_ID) { + return WM_drag_get_local_ID(drag, idcode); + } + + wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, idcode); + if (!asset_drag) { + return NULL; + } + + /* Link/append the asset. */ + return wm_drag_asset_id_import(asset_drag); } /* ************** draw ***************** */ @@ -342,7 +404,7 @@ static const char *wm_drag_name(wmDrag *drag) { switch (drag->type) { case WM_DRAG_ID: { - ID *id = WM_drag_ID(drag, 0); + ID *id = WM_drag_get_local_ID(drag, 0); bool single = (BLI_listbase_count_at_most(&drag->ids, 2) == 1); if (single) { @@ -353,6 +415,10 @@ static const char *wm_drag_name(wmDrag *drag) } break; } + case WM_DRAG_ASSET: { + const wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + return asset_drag->name; + } case WM_DRAG_PATH: case WM_DRAG_NAME: return drag->path; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index ac27862d507..c1ae307eb55 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1204,7 +1204,7 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, RNA_STRUCT_END; } else { - LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &op->opm->type->macro) { + LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) { wmOperatorType *otm = WM_operatortype_find(macro->idname, 0); wmOperator *opm = wm_operator_create(wm, otm, macro->ptr, NULL); @@ -2293,6 +2293,11 @@ static int wm_handler_fileselect_do(bContext *C, } wm_handler_op_context(C, handler, ctx_win->eventstate); + ScrArea *handler_area = CTX_wm_area(C); + /* Make sure new context area is ready, the operator callback may operate on it. */ + if (handler_area) { + ED_area_do_refresh(C, handler_area); + } /* Needed for #UI_popup_menu_reports. */ diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 5b21b2397e7..d179cc456f4 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -2959,30 +2959,25 @@ static uiBlock *block_create_autorun_warning(struct bContext *C, void *UNUSED(arg1)) { wmWindowManager *wm = CTX_wm_manager(C); - const uiStyle *style = UI_style_get_dpi(); - const int text_points_max = MAX2(style->widget.points, style->widgetlabel.points); - const int dialog_width = text_points_max * 44 * U.dpi_fac; uiBlock *block = UI_block_begin(C, region, "autorun_warning_popup", UI_EMBOSS); - UI_block_flag_enable( block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT); UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); UI_block_emboss_set(block, UI_EMBOSS); - uiLayout *layout = UI_block_layout( - block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 10, 2, dialog_width, 0, 0, style); + uiLayout *layout = uiItemsAlertBox(block, 44, ALERT_ICON_ERROR); - /* Text and some vertical space */ + /* Title and explanation text. */ uiLayout *col = uiLayoutColumn(layout, true); uiItemL_ex(col, TIP_("For security reasons, automatic execution of Python scripts " "in this file was disabled:"), - ICON_ERROR, + ICON_NONE, true, false); - uiItemL_ex(col, G.autoexec_fail, ICON_BLANK1, false, true); - uiItemL(col, TIP_("This may lead to unexpected behavior"), ICON_BLANK1); + uiItemL_ex(col, G.autoexec_fail, ICON_NONE, false, true); + uiItemL(col, TIP_("This may lead to unexpected behavior"), ICON_NONE); uiItemS(layout); @@ -2995,7 +2990,7 @@ static uiBlock *block_create_autorun_warning(struct bContext *C, TIP_("Permanently allow execution of scripts"), ICON_NONE); - uiItemS(layout); + uiItemS_ex(layout, 3.0f); /* Buttons */ uiBut *but; diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index 631a4d23eb5..fa658a5cdec 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -198,6 +198,8 @@ void WM_operator_properties_filesel(wmOperatorType *ot, ot->srna, "filter_blenlib", (filter & FILE_TYPE_BLENDERLIB) != 0, "Filter Blender IDs", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + /* TODO asset only filter? */ + prop = RNA_def_int( ot->srna, "filemode", diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index a5d23365df3..a5b5f082c41 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -3998,7 +3998,7 @@ static const EnumPropertyItem *rna_id_itemf(bool *r_free, /* Show collection color tag icons in menus. */ if (id_type == ID_GR) { - item_tmp.icon = UI_icon_color_from_collection((Collection *)id); + item_tmp.icon = UI_icon_color_from_collection((struct Collection *)id); } RNA_enum_item_add(&item, &totitem, &item_tmp); diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c index fb9c71163c8..a620accab72 100644 --- a/source/blender/windowmanager/intern/wm_stereo.c +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -188,7 +188,7 @@ void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy) } if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) { - const int half_x = win->sizex / 2; + const int half_x = WM_window_pixels_x(win) / 2; /* right half of the screen */ if (r_mouse_xy[0] > half_x) { r_mouse_xy[0] -= half_x; @@ -196,7 +196,7 @@ void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy) r_mouse_xy[0] *= 2; } else if (win->stereo3d_format->display_mode == S3D_DISPLAY_TOPBOTTOM) { - const int half_y = win->sizey / 2; + const int half_y = WM_window_pixels_y(win) / 2; /* upper half of the screen */ if (r_mouse_xy[1] > half_y) { r_mouse_xy[1] -= half_y; diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index baa47098bd3..c26acfc9802 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -23,7 +23,6 @@ #pragma once -struct ARegion; struct ReportList; struct wmWindow; diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index b48f4bdd4b9..7db936b3e51 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -29,11 +29,11 @@ blender_include_dirs( ../blender/blenloader ../blender/depsgraph ../blender/editors/include + ../blender/gpu ../blender/imbuf + ../blender/makesdna ../blender/makesrna ../blender/render - ../blender/gpu - ../blender/makesdna ../blender/windowmanager ) diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 97f5df3ec09..bf949d66286 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -205,6 +205,48 @@ add_blender_test( ) add_blender_test( + physics_dynamic_paint + ${TEST_SRC_DIR}/physics/dynamic_paint_test.blend + --python ${TEST_PYTHON_DIR}/physics_dynamic_paint.py + -- + --run-all-tests +) + +add_blender_test( + deform_modifiers + ${TEST_SRC_DIR}/modeling/deform_modifiers.blend + --python ${TEST_PYTHON_DIR}/deform_modifiers.py + -- + --run-all-tests +) + +add_blender_test( + physics_ocean + ${TEST_SRC_DIR}/physics/ocean_test.blend + --python ${TEST_PYTHON_DIR}/physics_ocean.py + -- + --run-all-tests +) + + +add_blender_test( + physics_particle_system + ${TEST_SRC_DIR}/physics/physics_particle_test.blend + --python ${TEST_PYTHON_DIR}/physics_particle_system.py + -- + --run-all-tests +) + +# Particle Instance disabling currently broken in master +# add_blender_test( +# physics_particle_instance +# ${TEST_SRC_DIR}/physics/physics_particle_instance.blend +# --python ${TEST_PYTHON_DIR}/physics_particle_instance.py +# -- +# --run-all-tests +# ) + +add_blender_test( constraints --python ${CMAKE_CURRENT_LIST_DIR}/bl_constraints.py -- diff --git a/tests/python/bevel_operator.py b/tests/python/bevel_operator.py index 50f52b958f7..c732d437b57 100644 --- a/tests/python/bevel_operator.py +++ b/tests/python/bevel_operator.py @@ -27,151 +27,287 @@ import os import sys sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import OperatorTest +from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest def main(): tests = [ # 0 - ['EDGE', {10}, 'Cube_test', 'Cube_result_1', 'bevel', {'offset': 0.2}], - ['EDGE', {10, 7}, 'Cube_test', 'Cube_result_2', 'bevel', {'offset': 0.2, 'offset_type': 'WIDTH'}], - ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_3', 'bevel', {'offset': 0.2, 'offset_type': 'DEPTH'}], - ['EDGE', {10}, 'Cube_test', 'Cube_result_4', 'bevel', {'offset': 0.4, 'segments': 2}], - ['EDGE', {10, 7}, 'Cube_test', 'Cube_result_5', 'bevel', {'offset': 0.4, 'segments': 3}], + MeshTest('Cube_test_1', 'Cube_test', 'Cube_result_1', + + [OperatorSpecEditMode('bevel', + {'offset': 0.2}, 'EDGE', {10})]), + MeshTest('Cube_test_2', 'Cube_test', 'Cube_result_2', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'offset_type': 'WIDTH'}, 'EDGE', {10, 7}, )]), + MeshTest('Cube_test_3', 'Cube_test', 'Cube_result_3', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'offset_type': 'DEPTH'}, 'EDGE', {8, 10, 7}, )]), + MeshTest('Cube_test_4', 'Cube_test', 'Cube_result_4', + [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 2}, 'EDGE', {10}, )]), + MeshTest('Cube_test_5', 'Cube_test', 'Cube_result_5', + [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 3}, 'EDGE', {10, 7}, )]), # 5 - ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_6', 'bevel', {'offset': 0.4, 'segments': 4}], - ['EDGE', {0, 10, 4, 7}, 'Cube_test', 'Cube_result_7', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 0.2}], - ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_8', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 0.25}], - ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_9', 'bevel', {'offset': 0.4, 'segments': 6, 'profile': 0.9}], - ['EDGE', {10, 7}, 'Cube_test', 'Cube_result_10', 'bevel', {'offset': 0.4, 'segments': 4, 'profile': 1.0}], + MeshTest('Cube_test_6', 'Cube_test', 'Cube_result_6', + [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 4}, 'EDGE', {8, 10, 7}, )]), + MeshTest('Cube_test_7', 'Cube_test', 'Cube_result_7', + [OperatorSpecEditMode('bevel', + {'offset': 0.4, 'segments': 5, 'profile': 0.2}, 'EDGE', {0, 10, 4, 7}, )]), + MeshTest('Cube_test_8', 'Cube_test', 'Cube_result_8', + [OperatorSpecEditMode('bevel', + {'offset': 0.4, 'segments': 5, 'profile': 0.25}, 'EDGE', {8, 10, 7}, )]), + MeshTest('Cube_test_9', 'Cube_test', 'Cube_result_9', + [OperatorSpecEditMode('bevel', + {'offset': 0.4, 'segments': 6, 'profile': 0.9}, 'EDGE', {8, 10, 7}, )]), + MeshTest('Cube_test_10', 'Cube_test', 'Cube_result_10', + [OperatorSpecEditMode('bevel', + {'offset': 0.4, 'segments': 4, 'profile': 1.0}, 'EDGE', {10, 7}, )]), # 10 - ['EDGE', {8, 10, 7}, 'Cube_test', 'Cube_result_11', 'bevel', {'offset': 0.4, 'segments': 5, 'profile': 1.0}], - ['EDGE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 'Cube_test', 'Cube_result_12', 'bevel', - {'offset': 0.4, 'segments': 8}], - ['EDGE', {5}, 'Pyr4_test', 'Pyr4_result_1', 'bevel', {'offset': 0.2}], - ['EDGE', {2, 5}, 'Pyr4_test', 'Pyr4_result_2', 'bevel', {'offset': 0.2}], - ['EDGE', {2, 3, 5}, 'Pyr4_test', 'Pyr4_result_3', 'bevel', {'offset': 0.2}], + MeshTest('Cube_test_11', 'Cube_test', 'Cube_result_11', + [OperatorSpecEditMode('bevel', + {'offset': 0.4, 'segments': 5, 'profile': 1.0}, 'EDGE', {8, 10, 7}, )]), + MeshTest("test 12", 'Cube_test', 'Cube_result_12', + [OperatorSpecEditMode('bevel', + {'offset': 0.4, 'segments': 8}, 'EDGE', + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, )]), + MeshTest('Pyramid4_test_1', 'Pyr4_test', 'Pyr4_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {5}, )]), + MeshTest('Pyramid4_test_2', 'Pyr4_test', 'Pyr4_result_2', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 5}, )]), + MeshTest('Pyramid4_test_3', 'Pyr4_test', 'Pyr4_result_3', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 3, 5}, )]), # 15 - ['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_4', 'bevel', {'offset': 0.2}], - ['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_5', 'bevel', {'offset': 0.2, 'segments': 3}], - ['EDGE', {2, 3}, 'Pyr4_test', 'Pyr4_result_6', 'bevel', {'offset': 0.2, 'segments': 2}], - ['EDGE', {1, 2, 3, 5}, 'Pyr4_test', 'Pyr4_result_7', 'bevel', {'offset': 0.2, 'segments': 4, 'profile': 0.15}], - ['VERT', {1}, 'Pyr4_test', 'Pyr4_result_8', 'bevel', {'offset': 0.75, 'segments': 4, 'affect': 'VERTICES'}], + MeshTest('Pyramid4_test_4', 'Pyr4_test', 'Pyr4_result_4', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {1, 2, 3, 5}, )]), + MeshTest('Pyramid4_test_5', 'Pyr4_test', 'Pyr4_result_5', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 2, 3, 5}, )]), + MeshTest('Pyramid4_test_6', 'Pyr4_test', 'Pyr4_result_6', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {2, 3}, )]), + MeshTest('Pyramid4_test_7', 'Pyr4_test', 'Pyr4_result_7', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 4, 'profile': 0.15}, 'EDGE', {1, 2, 3, 5}, )]), + MeshTest('Pyramid4_test_8', 'Pyr4_test', 'Pyr4_result_8', + [OperatorSpecEditMode('bevel', + {'offset': 0.75, 'segments': 4, 'affect': 'VERTICES'}, 'VERT', {1}, )]), # 20 - ['VERT', {1}, 'Pyr4_test', 'Pyr4_result_9', 'bevel', - {'offset': 0.75, 'segments': 3, 'affect': 'VERTICES', 'profile': 0.25}], - ['EDGE', {2, 3}, 'Pyr6_test', 'Pyr6_result_1', 'bevel', {'offset': 0.2}], - ['EDGE', {8, 2, 3}, 'Pyr6_test', 'Pyr6_result_2', 'bevel', {'offset': 0.2, 'segments': 2}], - ['EDGE', {0, 2, 3, 4, 6, 7, 9, 10, 11}, 'Pyr6_test', 'Pyr6_result_3', 'bevel', - {'offset': 0.2, 'segments': 4, 'profile': 0.8}], - ['EDGE', {8, 9, 3, 11}, 'Sept_test', 'Sept_result_1', 'bevel', {'offset': 0.1}], + MeshTest('Pyramid4_test_9', 'Pyr4_test', 'Pyr4_result_9', + [OperatorSpecEditMode('bevel', + {'offset': 0.75, 'segments': 3, 'affect': 'VERTICES', 'profile': 0.25}, 'VERT', + {1}, )]), + MeshTest('Pyramid6_test_1', 'Pyr6_test', 'Pyr6_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 3}, )]), + MeshTest('Pyramid6_test_2', 'Pyr6_test', 'Pyr6_result_2', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {8, 2, 3}, )]), + MeshTest('Pyramid6_test_3', 'Pyr6_test', 'Pyr6_result_3', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 4, 'profile': 0.8}, 'EDGE', + {0, 2, 3, 4, 6, 7, 9, 10, 11}, )]), + MeshTest('Sept_test_1', 'Sept_test', 'Sept_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.1}, 'EDGE', {8, 9, 3, 11}, )]), # 25 - ['EDGE', {8, 9, 11}, 'Sept_test', 'Sept_result_2', 'bevel', {'offset': 0.1, 'offset_type': 'WIDTH'}], - ['EDGE', {2, 8, 9, 12, 13, 14}, 'Saddle_test', 'Saddle_result_1', 'bevel', {'offset': 0.3, 'segments': 5}], - ['VERT', {4}, 'Saddle_test', 'Saddle_result_2', 'bevel', {'offset': 0.6, 'segments': 6, 'affect': 'VERTICES'}], - ['EDGE', {2, 5, 8, 11, 14, 18, 21, 24, 27, 30, 34, 37, 40, 43, 46, 50, 53, 56, 59, 62, 112, 113, 114, 115}, - 'Bent_test', 'Bent_result_1', 'bevel', {'offset': 0.2, 'segments': 3}], - ['EDGE', {1, 8, 9, 10, 11}, 'Bentlines_test', 'Bentlines_result_1', 'bevel', {'offset': 0.2, 'segments': 3}], + MeshTest('Sept_test_2', 'Sept_test', 'Sept_result_2', + [OperatorSpecEditMode('bevel', + {'offset': 0.1, 'offset_type': 'WIDTH'}, 'EDGE', {8, 9, 11}, )]), + MeshTest('Saddle_test_1', 'Saddle_test', 'Saddle_result_1', + [OperatorSpecEditMode('bevel', + {'offset': 0.3, 'segments': 5}, 'EDGE', {2, 8, 9, 12, 13, 14}, )]), + MeshTest('Saddle_test_2', 'Saddle_test', 'Saddle_result_2', + [OperatorSpecEditMode('bevel', + {'offset': 0.6, 'segments': 6, 'affect': 'VERTICES'}, 'VERT', {4}, )]), + + MeshTest('Bent_test', 'Bent_test', 'Bent_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, + 'EDGE', + {2, 5, 8, 11, 14, 18, 21, 24, 27, 30, 34, 37, 40, 43, 46, 50, 53, 56, 59, 62, + 112, 113, 114, 115}, )]), + MeshTest('Bentlines_test_1', 'Bentlines_test', 'Bentlines_result_1', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 8, 9, 10, 11}, )]), # 30 - ['EDGE', {26, 12, 20}, 'Flaretop_test', 'Flaretop_result_1', 'bevel', {'offset': 0.4, 'segments': 2}], - ['EDGE', {26, 12, 20}, 'Flaretop_test', 'Flaretop_result_2', 'bevel', - {'offset': 0.4, 'segments': 2, 'profile': 1.0}], - ['FACE', {1, 6, 7, 8, 9, 10, 11, 12}, 'Flaretop_test', 'Flaretop_result_3', 'bevel', - {'offset': 0.4, 'segments': 4}], - ['EDGE', {4, 8, 10, 18, 24}, 'BentL_test', 'BentL_result_1', 'bevel', {'offset': 0.2}], - ['EDGE', {0, 1, 2, 10}, 'Wires_test', 'Wires_test_result_1', 'bevel', {'offset': 0.3}], + MeshTest('Flaretop_test_1', 'Flaretop_test', 'Flaretop_result_1', + [OperatorSpecEditMode('bevel', + {'offset': 0.4, 'segments': 2}, 'EDGE', {26, 12, 20}, )]), + MeshTest('Flaretop_test_2', 'Flaretop_test', 'Flaretop_result_2', + [OperatorSpecEditMode('bevel', + {'offset': 0.4, 'segments': 2, 'profile': 1.0}, 'EDGE', {26, 12, 20}, )]), + MeshTest('Flaretop_test_3', 'Flaretop_test', 'Flaretop_result_3', + [OperatorSpecEditMode('bevel', + {'offset': 0.4, 'segments': 4}, 'FACE', {1, 6, 7, 8, 9, 10, 11, 12}, )]), + MeshTest('BentL_test', 'BentL_test', 'BentL_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {4, 8, 10, 18, 24}, )]), + MeshTest('Wires_test_1', 'Wires_test', 'Wires_test_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.3}, 'EDGE', {0, 1, 2, 10}, )]), # 35 - ['VERT', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 'Wires_test', 'Wires_test_result_2', 'bevel', - {'offset': 0.3, 'affect': 'VERTICES'}], - ['EDGE', {3, 4, 5}, 'tri', 'tri_result_1', 'bevel', {'offset': 0.2}], - ['EDGE', {3, 4, 5}, 'tri', 'tri_result_2', 'bevel', {'offset': 0.2, 'segments': 2}], - ['EDGE', {3, 4, 5}, 'tri', 'tri_result_3', 'bevel', {'offset': 0.2, 'segments': 3}], - ['EDGE', {3, 4}, 'tri', 'tri_result_4', 'bevel', {'offset': 0.2}], + MeshTest('Wires_test_2', 'Wires_test', 'Wires_test_result_2', + [OperatorSpecEditMode('bevel', + {'offset': 0.3, 'affect': 'VERTICES'}, 'VERT', + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, )]), + MeshTest('tri_test_1', 'tri', 'tri_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri_test_2', 'tri', 'tri_result_2', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri_test_3', 'tri', 'tri_result_3', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri_test_4', 'tri', 'tri_result_4', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]), # 40 - ['EDGE', {3, 4}, 'tri', 'tri_result_5', 'bevel', {'offset': 0.2, 'segments': 2}], - ['VERT', {3}, 'tri', 'tri_result_6', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}], - ['VERT', {3}, 'tri', 'tri_result_7', 'bevel', {'offset': 0.2, 'segments': 2, 'affect': 'VERTICES'}], - ['VERT', {3}, 'tri', 'tri_result_8', 'bevel', {'offset': 0.2, 'segments': 3, 'affect': 'VERTICES'}], - ['VERT', {1}, 'tri', 'tri_result_9', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}], + MeshTest('tri_test_5', 'tri', 'tri_result_5', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]), + MeshTest('tri_test_6', 'tri', 'tri_result_6', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {3}, )]), + MeshTest('tri_test_7', 'tri', 'tri_result_7', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 2, 'affect': 'VERTICES'}, 'VERT', {3}, )]), + MeshTest('tri_test_8', 'tri', 'tri_result_8', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 3, 'affect': 'VERTICES'}, 'VERT', {3}, )]), + MeshTest('tri_test_9', 'tri', 'tri_result_9', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {1}, )]), # 45 - ['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_1', 'bevel', {'offset': 0.2}], - ['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_2', 'bevel', {'offset': 0.2, 'segments': 2}], - ['EDGE', {3, 4, 5}, 'tri1gap', 'tri1gap_result_3', 'bevel', {'offset': 0.2, 'segments': 3}], - ['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_4', 'bevel', {'offset': 0.2}], - ['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_5', 'bevel', {'offset': 0.2, 'segments': 2}], + MeshTest('tri1gap_test_2', 'tri1gap', 'tri1gap_result_2', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri1gap_test_3', 'tri1gap', 'tri1gap_result_3', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri1gap_test_1', 'tri1gap', 'tri1gap_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri1gap_test_4', 'tri1gap', 'tri1gap_result_4', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]), + MeshTest('tri1gap_test_5', 'tri1gap', 'tri1gap_result_5', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]), # 50 - ['EDGE', {3, 4}, 'tri1gap', 'tri1gap_result_6', 'bevel', {'offset': 0.2, 'segments': 3}], - ['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_7', 'bevel', {'offset': 0.2}], - ['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_8', 'bevel', {'offset': 0.2, 'segments': 2}], - ['EDGE', {3, 5}, 'tri1gap', 'tri1gap_result_9', 'bevel', {'offset': 0.2, 'segments': 3}], - ['VERT', {3}, 'tri1gap', 'tri1gap_result_10', 'bevel', {'offset': 0.2, 'affect': 'VERTICES'}], + MeshTest('tri1gap_test_6', 'tri1gap', 'tri1gap_result_6', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4}, )]), + MeshTest('tri1gap_test_7', 'tri1gap', 'tri1gap_result_7', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 5}, )]), + MeshTest('tri1gap_test_8', 'tri1gap', 'tri1gap_result_8', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 5}, )]), + MeshTest('tri1gap_test_9', 'tri1gap', 'tri1gap_result_9', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 5}, )]), + MeshTest('tri1gap_test_10', 'tri1gap', 'tri1gap_result_10', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {3}, )]), # 55 - ['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_1', 'bevel', {'offset': 0.2}], - ['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_2', 'bevel', {'offset': 0.2, 'segments': 2}], - ['EDGE', {3, 4, 5}, 'tri2gaps', 'tri2gaps_result_3', 'bevel', {'offset': 0.2, 'segments': 3}], - ['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_4', 'bevel', {'offset': 0.2}], - ['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_5', 'bevel', {'offset': 0.2, 'segments': 2}], + MeshTest('tri2gaps_test_1', 'tri2gaps', 'tri2gaps_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri2gaps_test_2', 'tri2gaps', 'tri2gaps_result_2', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri2gaps_test_3', 'tri2gaps', 'tri2gaps_result_3', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri2gaps_test_4', 'tri2gaps', 'tri2gaps_result_4', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]), + MeshTest('tri2gaps_test_5', 'tri2gaps', 'tri2gaps_result_5', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]), # 60 - ['EDGE', {3, 4}, 'tri2gaps', 'tri2gaps_result_6', 'bevel', {'offset': 0.2, 'segments': 3}], - ['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_1', 'bevel', {'offset': 0.2}], - ['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_2', 'bevel', {'offset': 0.2, 'segments': 2}], - ['EDGE', {3, 4, 5}, 'tri3gaps', 'tri3gaps_result_3', 'bevel', {'offset': 0.2, 'segments': 3}], - ['EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, 'cube3', 'cube3_result_1', 'bevel', {'offset': 0.2}], + MeshTest('tri2gaps_test_6', 'tri2gaps', 'tri2gaps_result_6', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4}, )]), + MeshTest('tri3gaps_test_1', 'tri3gaps', 'tri3gaps_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri3gaps_test_2', 'tri3gaps', 'tri3gaps_result_2', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]), + MeshTest('tri3gaps_test_3', 'tri3gaps', 'tri3gaps_result_3', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]), + MeshTest('cube3_test_1', 'cube3', 'cube3_result_1', + [OperatorSpecEditMode('bevel', + {'offset': 0.2}, 'EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, )]), # 65 - ['EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, 'cube3', 'cube3_result_2', 'bevel', - {'offset': 0.2, 'segments': 2}], - ['EDGE', {32, 35}, 'cube3', 'cube3_result_3', 'bevel', {'offset': 0.2}], - ['EDGE', {24, 35}, 'cube3', 'cube3_result_4', 'bevel', {'offset': 0.2}], - ['EDGE', {24, 32, 35}, 'cube3', 'cube3_result_5', 'bevel', {'offset': 0.2, 'segments': 2}], - ['EDGE', {24, 32, 35}, 'cube3', 'cube3_result_6', 'bevel', {'offset': 0.2, 'segments': 3}], + MeshTest('cube3_test_2', 'cube3', 'cube3_result_2', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 2}, 'EDGE', + {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, )]), + MeshTest('cube3_test_3', 'cube3', 'cube3_result_3', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {32, 35}, )]), + MeshTest('cube3_test_4', 'cube3', 'cube3_result_4', + [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {24, 35}, )]), + MeshTest('cube3_test_5', 'cube3', 'cube3_result_5', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {24, 32, 35}, )]), + MeshTest('cube3_test_6', 'cube3', 'cube3_result_6', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {24, 32, 35}, )]), # 70 - ['EDGE', {0, 1, 6, 7, 12, 14, 16, 17}, 'Tray', 'Tray_result_1', 'bevel', {'offset': 0.01, 'segments': 2}], - ['EDGE', {33, 4, 38, 8, 41, 10, 42, 12, 14, 17, 24, 31}, 'Bumptop', 'Bumptop_result_1', 'bevel', - {'offset': 0.1, 'segments': 4}], - ['EDGE', {16, 14, 15}, 'Multisegment_test', 'Multisegment_result_1', 'bevel', {'offset': 0.2}], - ['EDGE', {16, 14, 15}, 'Multisegment_test', 'Multisegment_result_1', 'bevel', {'offset': 0.2}], - ['EDGE', {19, 20, 23, 15}, 'Window_test', 'Window_result_1', 'bevel', {'offset': 0.05, 'segments': 2}], + MeshTest('Tray', 'Tray', 'Tray_result_1', + [OperatorSpecEditMode('bevel', + {'offset': 0.01, 'segments': 2}, 'EDGE', {0, 1, 6, 7, 12, 14, 16, 17}, )]), + MeshTest("test 73", 'Bumptop', 'Bumptop_result_1', + [OperatorSpecEditMode('bevel', + {'offset': 0.1, 'segments': 4}, 'EDGE', + {33, 4, 38, 8, 41, 10, 42, 12, 14, 17, 24, 31}, )]), + MeshTest('Multisegment_test_1', 'Multisegment_test', 'Multisegment_result_1', + [OperatorSpecEditMode('bevel', + {'offset': 0.2}, 'EDGE', {16, 14, 15}, )]), + MeshTest('Window_test', 'Window_test', 'Window_result_1', + [OperatorSpecEditMode('bevel', + {'offset': 0.05, 'segments': 2}, 'EDGE', {19, 20, 23, 15}, )]), # 75 - ['EDGE', {8}, 'Cube_hn_test', 'Cube_hn_result_1', 'bevel', {'offset': 0.2, 'harden_normals': True}], - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_1', 'bevel', - {'offset': 0.2, 'miter_outer': 'PATCH'}], - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_2', 'bevel', - {'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH'}], - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_3', 'bevel', - {'offset': 0.2, 'segments': 3, 'miter_outer': 'PATCH'}], - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_4', 'bevel', - {'offset': 0.2, 'miter_outer': 'ARC'}], + MeshTest("test 77", 'Cube_hn_test', 'Cube_hn_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'harden_normals': True}, 'EDGE', {8}, )]), + MeshTest('Blocksteps_test_1', 'Blocksteps_test', 'Blocksteps_result_1', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'miter_outer': 'PATCH'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), + MeshTest('Blocksteps_test_2', 'Blocksteps_test', 'Blocksteps_result_2', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH'}, 'EDGE', + {4, 7, 39, 27, 30, 31}, )]), + MeshTest('Blocksteps_test_3', 'Blocksteps_test', 'Blocksteps_result_3', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 3, 'miter_outer': 'PATCH'}, 'EDGE', + {4, 7, 39, 27, 30, 31}, )]), + MeshTest('Blocksteps_test_4', 'Blocksteps_test', 'Blocksteps_result_4', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), # 80 - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_5', 'bevel', - {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}], - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_6', 'bevel', - {'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}], - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_7', 'bevel', - {'offset': 0.2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}], - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps_test', 'Blocksteps_result_8', 'bevel', - {'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}], - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps2_test', 'Blocksteps2_result_9', 'bevel', - {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}], + MeshTest('Blocksteps_test_5', 'Blocksteps_test', 'Blocksteps_result_5', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE', + {4, 7, 39, 27, 30, 31}, )]), + MeshTest('Blocksteps_test_6', 'Blocksteps_test', 'Blocksteps_result_6', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}, 'EDGE', + {4, 7, 39, 27, 30, 31}, )]), + MeshTest('Blocksteps_test_7', 'Blocksteps_test', 'Blocksteps_result_7', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}, 'EDGE', + {4, 7, 39, 27, 30, 31}, )]), + MeshTest("Blocksteps_test_8", 'Blocksteps_test', 'Blocksteps_result_8', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}, + 'EDGE', {4, 7, 39, 27, 30, 31}, )]), + MeshTest('Blocksteps2_test', 'Blocksteps2_test', 'Blocksteps2_result_9', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE', + {4, 7, 39, 27, 30, 31}, )]), # 85 - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps3_test', 'Blocksteps3_result_10', 'bevel', - {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}], - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps4_test', 'Blocksteps4_result_11', 'bevel', - {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}], - ['EDGE', {4, 7, 39, 27, 30, 31}, 'Blocksteps4_test', 'Blocksteps4_result_12', 'bevel', - {'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}], - ['EDGE', {1, 7}, 'Spike_test', 'Spike_result_1', 'bevel', {'offset': 0.2, 'segments': 3}] - ] + MeshTest('Blocksteps3_test', 'Blocksteps3_test', 'Blocksteps3_result_10', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE', + {4, 7, 39, 27, 30, 31}, )]), + MeshTest('Blocksteps4_test_1', 'Blocksteps4_test', 'Blocksteps4_result_11', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE', + {4, 7, 39, 27, 30, 31}, )]), + MeshTest('Blocksteps4_test_2', 'Blocksteps4_test', 'Blocksteps4_result_12', + [OperatorSpecEditMode('bevel', + {'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}, 'EDGE', + {4, 7, 39, 27, 30, 31}, )]), + MeshTest('Spike_test', 'Spike_test', 'Spike_result_1', + [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 7})]) - operator_test = OperatorTest(tests) + ] + operator_test = RunTest(tests) command = list(sys.argv) for i, cmd in enumerate(command): if cmd == "--run-all-tests": + operator_test.do_compare = True operator_test.run_all_tests() break elif cmd == "--run-test": - index = int(command[i + 1]) - operator_test.run_test(index) + name = command[i + 1] + operator_test.do_compare = False + operator_test.run_test(name) break diff --git a/tests/python/boolean_operator.py b/tests/python/boolean_operator.py index b35c69b7ca5..0db6a074699 100644 --- a/tests/python/boolean_operator.py +++ b/tests/python/boolean_operator.py @@ -29,33 +29,53 @@ import os import sys sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import OperatorTest +from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest def main(): tests = [ - ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}], - ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_2', 'intersect_boolean', {'operation': 'INTERSECT', 'solver' : 'FAST'}], - ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_3', 'intersect_boolean', {'operation': 'DIFFERENCE', 'solver' : 'FAST'}], - ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_4', 'intersect', {'separate_mode': 'CUT', 'solver' : 'FAST'}], - ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_5', 'intersect', {'separate_mode': 'ALL', 'solver' : 'FAST'}], - ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_6', 'intersect', {'separate_mode': 'NONE', 'solver' : 'FAST'}], - ['FACE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 'Cubecube', 'Cubecube_result_7', 'intersect', - {'mode': 'SELECT', 'separate_mode': 'NONE', 'solver' : 'FAST'}], - ['FACE', {6, 7, 8, 9, 10}, 'Cubecone', 'Cubecone_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}], - ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecones', 'Cubecones_result_1', 'intersect_boolean', {'operation': 'UNION', 'solver' : 'FAST'}], + + MeshTest('Cubecube_intersect_union', 'Cubecube', 'Cubecube_result_1', + [OperatorSpecEditMode('intersect_boolean', + {'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), + MeshTest('Cubecube_intersect_intersect', 'Cubecube', 'Cubecube_result_2', + [OperatorSpecEditMode('intersect_boolean', {'operation': 'INTERSECT', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), + MeshTest('Cubecube_intersect_difference', 'Cubecube', 'Cubecube_result_3', + [OperatorSpecEditMode('intersect_boolean', {'operation': 'DIFFERENCE', 'solver': 'FAST'}, 'FACE', + {0, 1, 2, 3, 4, 5}, )]), + MeshTest('Cubecube_intersect_cut', 'Cubecube', 'Cubecube_result_4', [OperatorSpecEditMode('intersect', + {'separate_mode': 'CUT', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), + MeshTest('Cubecube_intersect_all', 'Cubecube', 'Cubecube_result_5', + [OperatorSpecEditMode('intersect', + {'separate_mode': 'ALL', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), + MeshTest('Cubecube_intersect_none', 'Cubecube', 'Cubecube_result_6', + [OperatorSpecEditMode('intersect', + {'separate_mode': 'NONE', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), + MeshTest('Cubecube_intersect_select_none', 'Cubecube', + 'Cubecube_result_7', + [OperatorSpecEditMode('intersect', + {'mode': 'SELECT', 'separate_mode': 'NONE', 'solver': 'FAST'}, 'FACE', + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, )]), + MeshTest('Cubecone_intersect_union', 'Cubecone', 'Cubecone_result_1', + [OperatorSpecEditMode('intersect_boolean', + {'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {6, 7, 8, 9, 10}, )]), + MeshTest('Cubecones_intersect_union', 'Cubecones', 'Cubecones_result_1', + [OperatorSpecEditMode('intersect_boolean', {'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), + ] - operator_test = OperatorTest(tests) + operator_test = RunTest(tests) command = list(sys.argv) for i, cmd in enumerate(command): if cmd == "--run-all-tests": + operator_test.do_compare = True operator_test.run_all_tests() break elif cmd == "--run-test": - index = int(command[i + 1]) - operator_test.run_test(index) + name = command[i + 1] + operator_test.do_compare = False + operator_test.run_test(name) break diff --git a/tests/python/deform_modifiers.py b/tests/python/deform_modifiers.py new file mode 100644 index 00000000000..7c4ea457e9d --- /dev/null +++ b/tests/python/deform_modifiers.py @@ -0,0 +1,119 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +# To run the test type: blender -b /path/to/the/blend/file --python path/to/this/py/file -- --run-all-tests -- --verbose +# Type the above line in cmd/terminal, for example, look below +# blender -b c:\blender-lib\deform_modifiers.blend --python c:\deform_modifiers.py -- --run-all-tests -- --verbose + + +import os +import sys +import bpy + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import MeshTest, ModifierSpec, OperatorSpecObjectMode, DeformModifierSpec, RunTest + +tests = [ + + # Surface Deform Test, finally can bind to the Target object. + # Actual deformation occurs by animating imitating user input. + + MeshTest("SurfaceDeform", "testObjMonkeySurfaceDeform", "expObjMonkeySurfaceDeform", + [DeformModifierSpec(10, [ + ModifierSpec('surface_deform', 'SURFACE_DEFORM', {'target': bpy.data.objects["Cube"]})], + OperatorSpecObjectMode('surfacedeform_bind', {'modifier': 'surface_deform'}))]), + + # Mesh Deform Test, finally can bind to the Target object. + # Actual deformation occurs by animating imitating user input. + + MeshTest("MeshDeform", "testObjMonkeyMeshDeform", "expObjMonkeyMeshDeform", + [DeformModifierSpec(10, [ModifierSpec('mesh_deform', 'MESH_DEFORM', + {'object': bpy.data.objects["MeshCube"], 'precision': 2})], + OperatorSpecObjectMode('meshdeform_bind', {'modifier': 'mesh_deform'}))]), + + # Surface Deform Test, finally can bind to the Target object. + # Actual deformation occurs by animating imitating user input. + + MeshTest("Hook", "testObjHookPlane", "expObjHookPlane", + [DeformModifierSpec(10, [ModifierSpec('hook', 'HOOK', + {'object': bpy.data.objects["Empty"], 'falloff_radius': 1, + 'vertex_group': 'Group'})])]), + + # Laplacian Deform Test, first a hook is attached. + + MeshTest("Laplace", "testObjCubeLaplacian", "expObjCubeLaplacian", + [DeformModifierSpec(10, + [ModifierSpec('hook2', 'HOOK', {'object': bpy.data.objects["Empty.001"], + 'vertex_group': 'hook_vg'}), + ModifierSpec('laplace', 'LAPLACIANDEFORM', {'vertex_group': 'laplace_vg'})], + OperatorSpecObjectMode('laplaciandeform_bind', {'modifier': 'laplace'}))]), + + MeshTest("WarpPlane", "testObjPlaneWarp", "expObjPlaneWarp", + [DeformModifierSpec(10, [ModifierSpec('warp', 'WARP', + {'object_from': bpy.data.objects["From"], + 'object_to': bpy.data.objects["To"], + })])]), + + ############################################# + # Curves Deform Modifiers + ############################################# + MeshTest("CurveArmature", "testObjBezierCurveArmature", "expObjBezierCurveArmature", + [DeformModifierSpec(10, [ModifierSpec('curve_armature', 'ARMATURE', + {'object': bpy.data.objects['testArmatureHelper'], + 'use_vertex_groups': False, 'use_bone_envelopes': True})])]), + + MeshTest("CurveLattice", "testObjBezierCurveLattice", "expObjBezierCurveLattice", + [DeformModifierSpec(10, [ModifierSpec('curve_lattice', 'LATTICE', + {'object': bpy.data.objects['testLatticeCurve']})])]), + + # HOOK for Curves can't be tested with current framework, as it requires going to Edit Mode to select vertices, + # here is no equivalent of a vertex group in Curves. + # Dummy test for Hook, can also be called corner case + MeshTest("CurveHook", "testObjBezierCurveHook", "expObjBezierCurveHook", + [DeformModifierSpec(10, + [ModifierSpec('curve_Hook', 'HOOK', {'object': bpy.data.objects['EmptyCurve']})])]), + + MeshTest("MeshDeformCurve", "testObjCurveMeshDeform", "expObjCurveMeshDeform", + [DeformModifierSpec(10, [ + ModifierSpec('mesh_deform_curve', 'MESH_DEFORM', {'object': bpy.data.objects["Cylinder"], + 'precision': 2})], + OperatorSpecObjectMode('meshdeform_bind', {'modifier': 'mesh_deform_curve'}))]), + + MeshTest("WarpCurve", "testObjBezierCurveWarp", "expObjBezierCurveWarp", + [DeformModifierSpec(10, [ModifierSpec('warp_curve', 'WARP', + {'object_from': bpy.data.objects["From_curve"], + 'object_to': bpy.data.objects["To_curve"]})])]), + +] + +deform_tests = RunTest(tests) +command = list(sys.argv) +for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + deform_tests.apply_modifiers = True + deform_tests.do_compare = True + deform_tests.run_all_tests() + break + elif cmd == "--run-test": + deform_tests.apply_modifiers = False + deform_tests.do_compare = False + name = command[i + 1] + deform_tests.run_test(name) + break diff --git a/tests/python/modifiers.py b/tests/python/modifiers.py index ba156cef8ea..24f71c4066d 100644 --- a/tests/python/modifiers.py +++ b/tests/python/modifiers.py @@ -26,7 +26,7 @@ from random import shuffle, seed import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import ModifierTest, ModifierSpec +from modules.mesh_test import RunTest, ModifierSpec, MeshTest seed(0) @@ -47,7 +47,7 @@ def get_generate_modifiers_list(test_object_name, randomize=False): ModifierSpec('array', 'ARRAY', {}), ModifierSpec('bevel', 'BEVEL', {'width': 0.1}), ModifierSpec('boolean', 'BOOLEAN', {'object': boolean_test_object, 'solver': 'FAST'}), - ModifierSpec('build', 'BUILD', {'frame_start': 0, 'frame_duration': 1}), + ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2), ModifierSpec('decimate', 'DECIMATE', {}), ModifierSpec('edge split', 'EDGE_SPLIT', {}), @@ -62,7 +62,6 @@ def get_generate_modifiers_list(test_object_name, randomize=False): # ModifierSpec('remesh', 'REMESH', {}), # ModifierSpec('screw', 'SCREW', {}), # screw can make the test very slow. Skipping for now. - # ModifierSpec('skin', 'SKIN', {}), # skin is not reproducible . ModifierSpec('solidify', 'SOLIDIFY', {}), ModifierSpec('subsurf', 'SUBSURF', {}), @@ -78,7 +77,6 @@ def get_generate_modifiers_list(test_object_name, randomize=False): def main(): - mask_first_list = get_generate_modifiers_list("testCubeMaskFirst", randomize=True) mask_vertex_group = "testCubeMaskFirst" + "_mask" mask_first_list.insert(0, ModifierSpec('mask', 'MASK', {'vertex_group': mask_vertex_group})) @@ -88,169 +86,279 @@ def main(): # List of 'Generate' modifiers on a cube ############################### # 0 - # ["testCube", "expectedCube", get_generate_modifiers_list("testCube")], - ["testCubeRandom", "expectedCubeRandom", get_generate_modifiers_list("testCubeRandom", randomize=True)], - ["testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list], - - ["testCollapseDecimate", "expectedCollapseDecimate", - [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}), - ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'COLLAPSE', 'ratio': 0.25, 'use_collapse_triangulate': True})]], - ["testPlanarDecimate", "expectedPlanarDecimate", - [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}), - ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'DISSOLVE', 'angle_limit': math.radians(30)})]], - ["testUnsubdivideDecimate", "expectedUnsubdivideDecimate", - [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}), - ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'UNSUBDIV', 'iterations': 2})]], + # MeshTest("testCube", "expectedCube", get_generate_modifiers_list("testCube")), + MeshTest("CubeRandom", "testCubeRandom", "expectedCubeRandom", + get_generate_modifiers_list("testCubeRandom", randomize=True)), + MeshTest("CubeMaskFirst", "testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list), + + MeshTest("CollapseDecimate", "testCollapseDecimate", "expectedCollapseDecimate", + [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}), + ModifierSpec('decimate', 'DECIMATE', + {'decimate_type': 'COLLAPSE', 'ratio': 0.25, 'use_collapse_triangulate': True})]), + MeshTest("PlanarDecimate", "testPlanarDecimate", "expectedPlanarDecimate", + [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}), + ModifierSpec('decimate', 'DECIMATE', + {'decimate_type': 'DISSOLVE', 'angle_limit': math.radians(30)})]), + MeshTest("UnsubdivideDecimate", "testUnsubdivideDecimate", "expectedUnsubdivideDecimate", + [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2}), + ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'UNSUBDIV', 'iterations': 2})]), # 5 - ["testRadialBisectMirror", "expectedRadialBisectMirror", - [ModifierSpec('mirror1', 'MIRROR', {'use_bisect_axis': (True, False, False)}), - ModifierSpec('mirror2', 'MIRROR', {'use_bisect_axis': (True, False, False), 'mirror_object': bpy.data.objects["testRadialBisectMirrorHelper"]}), - ModifierSpec('mirror3', 'MIRROR', {'use_axis': (False, True, False), 'use_bisect_axis': (False, True, False), 'use_bisect_flip_axis': (False, True, False), 'mirror_object': bpy.data.objects["testRadialBisectMirrorHelper"]})]], - ["regressT58411Mirror", "expectedT58411Mirror", - [ModifierSpec('mirror', 'MIRROR', {}), - ModifierSpec('bevel', 'BEVEL', {'segments': 2, 'limit_method': 'WEIGHT'}), - ModifierSpec('subd', 'SUBSURF', {'levels': 1})]], - - ["testBasicScrew", "expectedBasicScrew", - [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testBasicScrewHelper"]}), - ModifierSpec("screw", 'SCREW', {'angle': math.radians(400), 'steps': 20, 'iterations': 2, 'screw_offset': 2, 'use_normal_calculate': True})]], - ["testObjectScrew", "expectedObjectScrew", - [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectScrewHelper2"]}), - ModifierSpec("screw", 'SCREW', {"angle": math.radians(600), 'steps': 32, 'iterations': 1, 'use_object_screw_offset': True, 'use_normal_calculate': True, 'object': bpy.data.objects["testObjectScrewHelper1"]})]], + MeshTest("RadialBisectMirror", "testRadialBisectMirror", "expectedRadialBisectMirror", + [ModifierSpec('mirror1', 'MIRROR', {'use_bisect_axis': (True, False, False)}), + ModifierSpec('mirror2', 'MIRROR', {'use_bisect_axis': (True, False, False), + 'mirror_object': bpy.data.objects[ + "testRadialBisectMirrorHelper"]}), + ModifierSpec('mirror3', 'MIRROR', + {'use_axis': (False, True, False), 'use_bisect_axis': (False, True, False), + 'use_bisect_flip_axis': (False, True, False), + 'mirror_object': bpy.data.objects["testRadialBisectMirrorHelper"]})]), + MeshTest("T58411Mirror", "regressT58411Mirror", "expectedT58411Mirror", + [ModifierSpec('mirror', 'MIRROR', {}), + ModifierSpec('bevel', 'BEVEL', {'segments': 2, 'limit_method': 'WEIGHT'}), + ModifierSpec('subd', 'SUBSURF', {'levels': 1})]), + + MeshTest("BasicScrew", "testBasicScrew", "expectedBasicScrew", + [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testBasicScrewHelper"]}), + ModifierSpec("screw", 'SCREW', + {'angle': math.radians(400), 'steps': 20, 'iterations': 2, 'screw_offset': 2, + 'use_normal_calculate': True})]), + MeshTest("ObjectScrew", "testObjectScrew", "expectedObjectScrew", + [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectScrewHelper2"]}), + ModifierSpec("screw", 'SCREW', + {"angle": math.radians(600), 'steps': 32, 'iterations': 1, + 'use_object_screw_offset': True, + 'use_normal_calculate': True, 'object': bpy.data.objects["testObjectScrewHelper1"]})]), # 9 - ["testMergedScrewWeld", "expectedMergedScrewWeld", - [ModifierSpec("screw", 'SCREW', {'angle': math.radians(360), 'steps': 12, 'iterations': 1, 'screw_offset': 1, 'use_normal_calculate': True, 'use_merge_vertices': True}), - ModifierSpec("weld", 'WELD', {"merge_threshold": 0.001})]], - ["regressT72380Weld", "expectedT72380Weld", - [ModifierSpec('vedit', 'VERTEX_WEIGHT_EDIT', {'vertex_group': 'Group', 'use_remove': True, 'remove_threshold': 1}), - ModifierSpec("weld", 'WELD', {"merge_threshold": 0.2, "vertex_group": "Group"})]], - ["regressT72792Weld", "expectedT72792Weld", - [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 2}), - ModifierSpec("weld", 'WELD', {"merge_threshold": 0.1, "vertex_group": "Group"})]], + MeshTest("MergedScrewWeld", "testMergedScrewWeld", "expectedMergedScrewWeld", + [ModifierSpec("screw", 'SCREW', + {'angle': math.radians(360), 'steps': 12, 'iterations': 1, 'screw_offset': 1, + 'use_normal_calculate': True, 'use_merge_vertices': True}), + ModifierSpec("weld", 'WELD', {"merge_threshold": 0.001})]), + MeshTest("T72380Weld", "regressT72380Weld", "expectedT72380Weld", + [ModifierSpec('vedit', 'VERTEX_WEIGHT_EDIT', + {'vertex_group': 'Group', 'use_remove': True, 'remove_threshold': 1}), + ModifierSpec("weld", 'WELD', {"merge_threshold": 0.2, "vertex_group": "Group"})]), + MeshTest("T72792Weld", "regressT72792Weld", "expectedT72792Weld", + [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 2}), + ModifierSpec("weld", 'WELD', {"merge_threshold": 0.1, "vertex_group": "Group"})]), ############################################ # One 'Generate' modifier on primitive meshes ############################################# # 12 - ["testCubeArray", "expectedCubeArray", [ModifierSpec('array', 'ARRAY', {})]], - ["testCapArray", "expectedCapArray", - [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIT_LENGTH', 'fit_length': 2.0, 'start_cap': bpy.data.objects["testCapStart"], 'end_cap': bpy.data.objects["testCapEnd"]})]], - ["testCurveArray", "expectedCurveArray", - [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIT_CURVE', 'curve': bpy.data.objects["testCurveArrayHelper"], 'use_relative_offset': False, 'use_constant_offset': True, 'constant_offset_displace': (0.5, 0, 0)})]], - ["testRadialArray", "expectedRadialArray", - [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 3, 'use_merge_vertices': True, 'use_merge_vertices_cap': True, 'use_relative_offset': False, 'use_object_offset': True, 'offset_object': bpy.data.objects["testRadialArrayHelper"]})]], - - ["testCylinderBuild", "expectedCylinderBuild", [ModifierSpec('build', 'BUILD', {'frame_start': 0, 'frame_duration': 1})]], + MeshTest("CubeArray", "testCubeArray", "expectedCubeArray", + [ModifierSpec('array', 'ARRAY', {})]), + MeshTest("CapArray", "testCapArray", "expectedCapArray", + [ModifierSpec('array', 'ARRAY', + {'fit_type': 'FIT_LENGTH', 'fit_length': 2.0, + 'start_cap': bpy.data.objects["testCapStart"], + 'end_cap': bpy.data.objects["testCapEnd"]})]), + MeshTest("CurveArray", "testCurveArray", "expectedCurveArray", + [ModifierSpec('array', 'ARRAY', + {'fit_type': 'FIT_CURVE', 'curve': bpy.data.objects["testCurveArrayHelper"], + 'use_relative_offset': False, 'use_constant_offset': True, + 'constant_offset_displace': (0.5, 0, 0)})]), + MeshTest("RadialArray", "testRadialArray", "expectedRadialArray", + [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 3, 'use_merge_vertices': True, + 'use_merge_vertices_cap': True, 'use_relative_offset': False, + 'use_object_offset': True, + 'offset_object': bpy.data.objects["testRadialArrayHelper"]})]), + + MeshTest("CylinderBuild", "testCylinderBuild", "expectedCylinderBuild", + [ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2)]), # 17 - ["testConeDecimate", "expectedConeDecimate", [ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]], - ["testCubeEdgeSplit", "expectedCubeEdgeSplit", [ModifierSpec('edge split', 'EDGE_SPLIT', {})]], - - ["testSphereMirror", "expectedSphereMirror", [ModifierSpec('mirror', 'MIRROR', {})]], - ["testLocalMirror", "expectedLocalMirror", - [ModifierSpec('mirror', 'MIRROR', {'use_clip': True})]], - ["testObjectOffsetMirror", "expectedObjectOffsetMirror", - [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectOffsetMirrorHelper"]})]], - - ["testCylinderMask", "expectedCylinderMask", [ModifierSpec('mask', 'MASK', {'vertex_group': "mask_vertex_group"})]], - ["testConeMultiRes", "expectedConeMultiRes", [ModifierSpec('multires', 'MULTIRES', {})]], + MeshTest("ConeDecimate", "testConeDecimate", "expectedConeDecimate", + [ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]), + MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit", + [ModifierSpec('edge split', 'EDGE_SPLIT', {})]), + + MeshTest("SphereMirror", "testSphereMirror", "expectedSphereMirror", + [ModifierSpec('mirror', 'MIRROR', {})]), + MeshTest("LocalMirror", "testLocalMirror", "expectedLocalMirror", + [ModifierSpec('mirror', 'MIRROR', {'use_clip': True})]), + MeshTest("ObjectOffsetMirror", "testObjectOffsetMirror", "expectedObjectOffsetMirror", + [ModifierSpec('mirror', 'MIRROR', + {'mirror_object': bpy.data.objects["testObjectOffsetMirrorHelper"]})]), + + MeshTest("CylinderMask", "testCylinderMask", "expectedCylinderMask", + [ModifierSpec('mask', 'MASK', {'vertex_group': "mask_vertex_group"})]), + MeshTest("ConeMultiRes", "testConeMultiRes", "expectedConeMultiRes", + [ModifierSpec('multires', 'MULTIRES', {})]), # 24 - ["testCubeScrew", "expectedCubeScrew", [ModifierSpec('screw', 'SCREW', {})]], - - ["testCubeSolidify", "expectedCubeSolidify", [ModifierSpec('solidify', 'SOLIDIFY', {})]], - ["testComplexSolidify", "expectedComplexSolidify", - [ModifierSpec('solidify', 'SOLIDIFY', {'solidify_mode': 'NON_MANIFOLD', 'thickness': 0.05, 'offset': 0, 'nonmanifold_thickness_mode': 'CONSTRAINTS'})]], - ["regressT63063Solidify", "expectedT63063Solidify", - [ModifierSpec('solid', 'SOLIDIFY', {'thickness': 0.1, 'offset': 0.7})]], - ["regressT61979Solidify", "expectedT61979Solidify", - [ModifierSpec('solid', 'SOLIDIFY', {'thickness': -0.25, 'use_even_offset': True, 'use_quality_normals': True})]], - - ["testMonkeySubsurf", "expectedMonkeySubsurf", [ModifierSpec('subsurf', 'SUBSURF', {})]], - ["testCatmullClarkSubdivisionSurface", "expectedCatmullClarkSubdivisionSurface", - [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]], - ["testSimpleSubdivisionSurface", "expectedSimpleSubdivisionSurface", - [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2, 'subdivision_type': 'SIMPLE'})]], - ["testCrease2dSubdivisionSurface", "expectedCrease2dSubdivisionSurface", - [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]], - ["testCrease3dSubdivisionSurface", "expectedCrease3dSubdivisionSurface", - [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]], + MeshTest("CubeScrew", "testCubeScrew", "expectedCubeScrew", + [ModifierSpec('screw', 'SCREW', {})]), + + MeshTest("CubeSolidify", "testCubeSolidify", "expectedCubeSolidify", + [ModifierSpec('solidify', 'SOLIDIFY', {})]), + MeshTest("ComplexSolidify", "testComplexSolidify", "expectedComplexSolidify", + [ModifierSpec('solidify', 'SOLIDIFY', {'solidify_mode': 'NON_MANIFOLD', 'thickness': 0.05, 'offset': 0, + 'nonmanifold_thickness_mode': 'CONSTRAINTS'})]), + MeshTest("T63063Solidify", "regressT63063Solidify", "expectedT63063Solidify", + [ModifierSpec('solid', 'SOLIDIFY', {'thickness': 0.1, 'offset': 0.7})]), + MeshTest("T61979Solidify", "regressT61979Solidify", "expectedT61979Solidify", + [ModifierSpec('solid', 'SOLIDIFY', + {'thickness': -0.25, 'use_even_offset': True, 'use_quality_normals': True})]), + + MeshTest("MonkeySubsurf", "testMonkeySubsurf", "expectedMonkeySubsurf", + [ModifierSpec('subsurf', 'SUBSURF', {})]), + MeshTest("CatmullClarkSubdivisionSurface", "testCatmullClarkSubdivisionSurface", + "expectedCatmullClarkSubdivisionSurface", + [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]), + MeshTest("SimpleSubdivisionSurface", "testSimpleSubdivisionSurface", "expectedSimpleSubdivisionSurface", + [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2, 'subdivision_type': 'SIMPLE'})]), + MeshTest("Crease2dSubdivisionSurface", "testCrease2dSubdivisionSurface", "expectedCrease2dSubdivisionSurface", + [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]), + MeshTest("Crease3dSubdivisionSurface", "testCrease3dSubdivisionSurface", "expectedCrease3dSubdivisionSurface", + [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]), # 34 - ["testSphereTriangulate", "expectedSphereTriangulate", [ModifierSpec('triangulate', 'TRIANGULATE', {})]], - ["testMonkeyWireframe", "expectedMonkeyWireframe", [ModifierSpec('wireframe', 'WIREFRAME', {})]], - #ModifierSpec('skin', 'SKIN', {}), # skin is not reproducible . - ["testMergedWeld", "expectedMergedWeld", - [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.021})]], - ["testMergedAllWeld", "expectedMergedAllWeld", - [ModifierSpec("weld", 'WELD', {"merge_threshold": 1.8})]], - ["testMergedNoneWeld", "expectedMergedNoneWeld", - [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.019})]], + MeshTest("SphereTriangulate", "testSphereTriangulate", "expectedSphereTriangulate", + [ModifierSpec('triangulate', 'TRIANGULATE', {})]), + MeshTest("MonkeyWireframe", "testMonkeyWireframe", "expectedMonkeyWireframe", + [ModifierSpec('wireframe', 'WIREFRAME', {})]), + + # Duplicate the object, test object and expected object have same world coordinates. + MeshTest("Skin", "testObjPlaneSkin", "expObjPlaneSkin", + [ModifierSpec('skin', 'SKIN', {})]), + + MeshTest("MergedWeld", "testMergedWeld", "expectedMergedWeld", + [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.021})]), + MeshTest("MergedAllWeld", "testMergedAllWeld", "expectedMergedAllWeld", + [ModifierSpec("weld", 'WELD', {"merge_threshold": 1.8})]), + MeshTest("MergedNoneWeld", "testMergedNoneWeld", "expectedMergedNoneWeld", + [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.019})]), + ############################################# # One 'Deform' modifier on primitive meshes ############################################# # 39 - ["testMonkeyArmature", "expectedMonkeyArmature", - [ModifierSpec('armature', 'ARMATURE', {'object': bpy.data.objects['testArmature'], 'use_vertex_groups': True})]], - ["testTorusCast", "expectedTorusCast", [ModifierSpec('cast', 'CAST', {'factor': 2.64})]], - ["testCubeCurve", "expectedCubeCurve", - [ModifierSpec('curve', 'CURVE', {'object': bpy.data.objects['testBezierCurve']})]], - ["testMonkeyDisplace", "expectedMonkeyDisplace", [ModifierSpec('displace', "DISPLACE", {})]], - - # Hook modifier requires moving the hook object to get a mesh change, so can't test it with the current framework - # ["testMonkeyHook", "expectedMonkeyHook", - # [ModifierSpec('hook', 'HOOK', {'object': bpy.data.objects["EmptyHook"], 'vertex_group': "HookVertexGroup"})]], + MeshTest("MonkeyArmature", "testMonkeyArmature", "expectedMonkeyArmature", + [ModifierSpec('armature', 'ARMATURE', + {'object': bpy.data.objects['testArmature'], 'use_vertex_groups': True})]), + MeshTest("TorusCast", "testTorusCast", "expectedTorusCast", + [ModifierSpec('cast', 'CAST', {'factor': 2.64})]), + MeshTest("CubeCurve", "testCubeCurve", "expectedCubeCurve", + [ModifierSpec('curve', 'CURVE', {'object': bpy.data.objects['testBezierCurve']})]), + MeshTest("MonkeyDisplace", "testMonkeyDisplace", "expectedMonkeyDisplace", + [ModifierSpec('displace', "DISPLACE", {})]), + + # Hook modifier requires moving the hook object to get a mesh change + # so can't test it with the current framework + # MeshTest("MonkeyHook", "testMonkeyHook", "expectedMonkeyHook", + # [ModifierSpec('hook', 'HOOK', {'object': bpy.data.objects["EmptyHook"], 'vertex_group': + # "HookVertexGroup"})]), # 43 - #ModifierSpec('laplacian_deform', 'LAPLACIANDEFORM', {}) Laplacian requires a more complex mesh - ["testCubeLattice", "expectedCubeLattice", - [ModifierSpec('lattice', 'LATTICE', {'object': bpy.data.objects["testLattice"]})]], - # ModifierSpec('laplacian_deform', 'LAPLACIANDEFORM', {}) Laplacian requires a more complex mesh + MeshTest("CubeLattice", "testCubeLattice", "expectedCubeLattice", + [ModifierSpec('lattice', 'LATTICE', {'object': bpy.data.objects["testLattice"]})]), + + MeshTest("PlaneShrinkWrap", "testPlaneShrinkWrap", "expectedPlaneShrinkWrap", + [ModifierSpec('shrinkwrap', 'SHRINKWRAP', + {'target': bpy.data.objects["testCubeWrap"], 'offset': 0.5})]), - # Mesh Deform Modifier requires user input, so skip. + MeshTest("CylinderSimpleDeform", "testCylinderSimpleDeform", "expectedCylinderSimpleDeform", + [ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(180), 'deform_axis': 'Z'})]), - # mesh_test = MeshTest("testMonkeyDeform", "expectedMonkeyDeform",[ - # ModifierSpec('mesh_deform', 'MESH_DEFORM', {'object': bpy.data.objects["testDeformStructure"]}), - # OperatorSpec('meshdeform_bind',{'modifier':'MeshDeform'},'FACE',{i for in range(500)}) - # ] ,True) + MeshTest("PlaneSmooth", "testPlaneSmooth", "expectedPlaneSmooth", + [ModifierSpec('smooth', 'SMOOTH', {'iterations': 11})]), - ["testPlaneShrinkWrap", "expectedPlaneShrinkWrap", - [ModifierSpec('shrinkwrap', 'SHRINKWRAP', {'target': bpy.data.objects["testCubeWrap"], 'offset': 0.5})]], + # Smooth corrective requires a complex mesh. - ["testCylinderSimpleDeform", "expectedCylinderSimpleDeform", - [ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(180), 'deform_axis': 'Z'})]], + MeshTest("BalloonLaplacianSmooth", "testBalloonLaplacianSmooth", "expectedBalloonLaplacianSmooth", + [ModifierSpec('laplaciansmooth', 'LAPLACIANSMOOTH', {'lambda_factor': 12, 'lambda_border': 12})]), - ["testPlaneSmooth", "expectedPlaneSmooth", - [ModifierSpec('smooth', 'SMOOTH', {'iterations': 11})]], + # Gets updated often + MeshTest("WavePlane", "testObjPlaneWave", "expObjPlaneWave", + [ModifierSpec('wave', 'WAVE', {})]), - # Smooth corrective requires a complex mesh. + ############################################# + # CURVES Generate Modifiers + ############################################# + # Caution: Make sure test object has no modifier in "added" state, the test may fail. + MeshTest("BezCurveArray", "testObjBezierCurveArray", "expObjBezierCurveArray", + [ModifierSpec('array', 'ARRAY', {})]), + + MeshTest("CurveBevel", "testObjBezierCurveBevel", "expObjBezierCurveBevel", + [ModifierSpec('bevel', 'BEVEL', {})]), + + MeshTest("CurveBuild", "testObjBezierCurveBuild", "expObjBezierCurveBuild", + [ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2)]), + + MeshTest("CurveDecimate", "testObjBezierCurveDecimate", "expObjBezierCurveDecimate", + [ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]), + + MeshTest("CurveEdgeSplit", "testObjBezierCurveEdgeSplit", "expObjBezierCurveEdgeSplit", + [ModifierSpec('edgeSplit', 'EDGE_SPLIT', {})]), + + MeshTest("CurveMirror", "testObjBezierCurveMirror", "expObjBezierCurveMirror", + [ModifierSpec('mirror', 'MIRROR', {'use_axis': (True, True, False)})]), + + MeshTest("CurveScrew", "testObjBezierCurveScrew", "expObjBezierCurveScrew", + [ModifierSpec('screw', 'SCREW', {})]), + + MeshTest("CurveSolidify", "testObjBezierCurveSolidify", "expObjBezierCurveSolidify", + [ModifierSpec('solidify', 'SOLIDIFY', {'thickness': 1})]), + + MeshTest("CurveSubSurf", "testObjBezierCurveSubSurf", "expObjBezierCurveSubSurf", + [ModifierSpec('subSurf', 'SUBSURF', {})]), + + MeshTest("CurveTriangulate", "testObjBezierCurveTriangulate", "expObjBezierCurveTriangulate", + [ModifierSpec('triangulate', 'TRIANGULATE', {})]), + + # Test 60 + # Caution Weld: if the distance is increased beyond a limit, the object disappears + MeshTest("CurveWeld", "testObjBezierCurveWeld", "expObjBezierCurveWeld", + [ModifierSpec('weld', 'WELD', {})]), + + MeshTest("CurveWeld2", "testObjBezierCurveWeld2", "expObjBezierCurveWeld2", + [ModifierSpec('weld', 'WELD', {})]), + + ############################################# + # Curves Deform Modifiers + ############################################# + # Test 62 + MeshTest("CurveCast", "testObjBezierCurveCast", "expObjBezierCurveCast", + [ModifierSpec('Cast', 'CAST', {'cast_type': 'CYLINDER', 'factor': 10})]), + + MeshTest("CurveShrinkWrap", "testObjBezierCurveShrinkWrap", "expObjBezierCurveShrinkWrap", + [ModifierSpec('ShrinkWrap', 'SHRINKWRAP', + {'target': bpy.data.objects['testShrinkWrapHelperSuzanne']})]), + + MeshTest("CurveSimpleDeform", "testObjBezierCurveSimpleDeform", "expObjBezierCurveSimpleDeform", + [ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(90)})]), - ["testBalloonLaplacianSmooth", "expectedBalloonLaplacianSmooth", - [ModifierSpec('laplaciansmooth', 'LAPLACIANSMOOTH', {'lambda_factor': 12, 'lambda_border': 12})]], + MeshTest("CurveSmooth", "testObjBezierCurveSmooth", "expObjBezierCurveSmooth", + [ModifierSpec('smooth', 'SMOOTH', {'factor': 10})]), - # Surface Deform and Warp requires user input, so skip. + MeshTest("CurveWave", "testObjBezierCurveWave", "expObjBezierCurveWave", + [ModifierSpec('curve_wave', 'WAVE', {'time_offset': -1.5})]), - # Wave - requires complex mesh, so skip. + MeshTest("CurveCurve", "testObjBezierCurveCurve", "expObjBezierCurveCurve", + [ModifierSpec('curve_Curve', 'CURVE', {'object': bpy.data.objects['NurbsCurve']})]), ] - modifiers_test = ModifierTest(tests) + modifiers_test = RunTest(tests) command = list(sys.argv) for i, cmd in enumerate(command): if cmd == "--run-all-tests": modifiers_test.apply_modifiers = True + modifiers_test.do_compare = True modifiers_test.run_all_tests() break elif cmd == "--run-test": modifiers_test.apply_modifiers = False - index = int(command[i + 1]) - modifiers_test.run_test(index) + modifiers_test.do_compare = False + name = command[i + 1] + modifiers_test.run_test(name) break diff --git a/tests/python/modules/mesh_test.py b/tests/python/modules/mesh_test.py index c85e7acf4e8..6671918a206 100644 --- a/tests/python/modules/mesh_test.py +++ b/tests/python/modules/mesh_test.py @@ -45,7 +45,6 @@ import functools import inspect import os - # Output from this module and from blender itself will occur during tests. # We need to flush python so that the output is properly interleaved, otherwise # blender's output for one test will end up showing in the middle of another test... @@ -54,37 +53,39 @@ print = functools.partial(print, flush=True) class ModifierSpec: """ - Holds one modifier and its parameters. + Holds a Generate or Deform or Physics modifier type and its parameters. """ - def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict): + def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict, frame_end=0): """ Constructs a modifier spec. :param modifier_name: str - name of object modifier, e.g. "myFirstSubsurfModif" :param modifier_type: str - type of object modifier, e.g. "SUBSURF" :param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"quality" : 4} + :param frame_end: int - frame at which simulation needs to be baked or modifier needs to be applied. """ self.modifier_name = modifier_name self.modifier_type = modifier_type self.modifier_parameters = modifier_parameters + self.frame_end = frame_end def __str__(self): return "Modifier: " + self.modifier_name + " of type " + self.modifier_type + \ " with parameters: " + str(self.modifier_parameters) -class PhysicsSpec: +class ParticleSystemSpec: """ - Holds one Physics modifier and its parameters. + Holds a Particle System modifier and its parameters. """ def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict, frame_end: int): """ - Constructs a physics spec. - :param modifier_name: str - name of object modifier, e.g. "Cloth" - :param modifier_type: str - type of object modifier, e.g. "CLOTH" - :param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"quality" : 4} - :param frame_end:int - the last frame of the simulation at which it is baked + Constructs a particle system spec. + :param modifier_name: str - name of object modifier, e.g. "Particles" + :param modifier_type: str - type of object modifier, e.g. "PARTICLE_SYSTEM" + :param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"seed" : 1} + :param frame_end: int - the last frame of the simulation at which the modifier is applied """ self.modifier_name = modifier_name self.modifier_type = modifier_type @@ -96,14 +97,14 @@ class PhysicsSpec: " with parameters: " + str(self.modifier_parameters) + " with frame end: " + str(self.frame_end) -class OperatorSpec: +class OperatorSpecEditMode: """ Holds one operator and its parameters. """ def __init__(self, operator_name: str, operator_parameters: dict, select_mode: str, selection: set): """ - Constructs an operatorSpec. Raises ValueError if selec_mode is invalid. + Constructs an OperatorSpecEditMode. Raises ValueError if selec_mode is invalid. :param operator_name: str - name of mesh operator from bpy.ops.mesh, e.g. "bevel" or "fill" :param operator_parameters: dict - {name : val} dictionary containing operator parameters. :param select_mode: str - mesh selection mode, must be either 'VERT', 'EDGE' or 'FACE' @@ -121,6 +122,45 @@ class OperatorSpec: " in selection mode: " + self.select_mode + ", selecting " + str(self.selection) +class OperatorSpecObjectMode: + """ + Holds an object operator and its parameters. Helper class for DeformModifierSpec. + Needed to support operations in Object Mode and not Edit Mode which is supported by OperatorSpecEditMode. + """ + + def __init__(self, operator_name: str, operator_parameters: dict): + """ + :param operator_name: str - name of the object operator from bpy.ops.object, e.g. "shade_smooth" or "shape_keys" + :param operator_parameters: dict - contains operator parameters. + """ + self.operator_name = operator_name + self.operator_parameters = operator_parameters + + def __str__(self): + return "Operator: " + self.operator_name + " with parameters: " + str(self.operator_parameters) + + +class DeformModifierSpec: + """ + Holds a list of deform modifier and OperatorSpecObjectMode. + For deform modifiers which have an object operator + """ + + def __init__(self, frame_number: int, modifier_list: list, object_operator_spec: OperatorSpecObjectMode = None): + """ + Constructs a Deform Modifier spec (for user input) + :param frame_number: int - the frame at which animated keyframe is inserted + :param modifier_list: ModifierSpec - contains modifiers + :param object_operator_spec: OperatorSpecObjectMode - contains object operators + """ + self.frame_number = frame_number + self.modifier_list = modifier_list + self.object_operator_spec = object_operator_spec + + def __str__(self): + return "Modifier: " + str(self.modifier_list) + " with object operator " + str(self.object_operator_spec) + + class MeshTest: """ A mesh testing class targeted at testing modifiers and operators on a single object. @@ -129,33 +169,45 @@ class MeshTest: """ def __init__( - self, - test_object_name: str, - expected_object_name: str, - operations_stack=None, - apply_modifiers=False, - threshold=None, + self, + test_name: str, + test_object_name: str, + expected_object_name: str, + operations_stack=None, + apply_modifiers=False, + do_compare=False, + threshold=None ): """ Constructs a MeshTest object. Raises a KeyError if objects with names expected_object_name or test_object_name don't exist. - :param test_object: str - Name of object of mesh type to run the operations on. - :param expected_object: str - Name of object of mesh type that has the expected + :param test_name: str - unique test name identifier. + :param test_object_name: str - Name of object of mesh type to run the operations on. + :param expected_object_name: str - Name of object of mesh type that has the expected geometry after running the operations. :param operations_stack: list - stack holding operations to perform on the test_object. - :param apply_modifier: bool - True if we want to apply the modifiers right after adding them to the object. - This affects operations of type ModifierSpec only. + :param apply_modifiers: bool - True if we want to apply the modifiers right after adding them to the object. + - True if we want to apply the modifier to a list of modifiers, after some operation. + This affects operations of type ModifierSpec and DeformModifierSpec. + :param do_compare: bool - True if we want to compare the test and expected objects, False otherwise. + :param threshold : exponent: To allow variations and accept difference to a certain degree. + """ if operations_stack is None: operations_stack = [] for operation in operations_stack: - if not (isinstance(operation, ModifierSpec) or isinstance(operation, OperatorSpec)): - raise ValueError("Expected operation of type {} or {}. Got {}". - format(type(ModifierSpec), type(OperatorSpec), + if not (isinstance(operation, ModifierSpec) or isinstance(operation, OperatorSpecEditMode) + or isinstance(operation, OperatorSpecObjectMode) or isinstance(operation, DeformModifierSpec) + or isinstance(operation, ParticleSystemSpec)): + raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}". + format(type(ModifierSpec), type(OperatorSpecEditMode), + type(DeformModifierSpec), type(ParticleSystemSpec), type(operation))) self.operations_stack = operations_stack self.apply_modifier = apply_modifiers + self.do_compare = do_compare self.threshold = threshold + self.test_name = test_name self.verbose = os.environ.get("BLENDER_VERBOSE") is not None self.update = os.getenv('BLENDER_TEST_UPDATE') is not None @@ -187,22 +239,6 @@ class MeshTest: objects = bpy.data.objects self.expected_object = objects[expected_object_name] - def add_modifier(self, modifier_spec: ModifierSpec): - """ - Add a modifier to the operations stack. - :param modifier_spec: modifier to add to the operations stack - """ - self.operations_stack.append(modifier_spec) - if self.verbose: - print("Added modififier {}".format(modifier_spec)) - - def add_operator(self, operator_spec: OperatorSpec): - """ - Adds an operator to the operations stack. - :param operator_spec: OperatorSpec - operator to add to the operations stack. - """ - self.operations_stack.append(operator_spec) - def _on_failed_test(self, compare_result, validation_success, evaluated_test_object): if self.update and validation_success: if self.verbose: @@ -239,83 +275,164 @@ class MeshTest: """ return self._test_updated - def _apply_modifier(self, test_object, modifier_spec: ModifierSpec): + def _set_parameters_impl(self, modifier, modifier_parameters, nested_settings_path, modifier_name): """ - Add modifier to object and apply (if modifier_spec.apply_modifier is True) + Doing a depth first traversal of the modifier parameters and setting their values. + :param: modifier: Of type modifier, its altered to become a setting in recursion. + :param: modifier_parameters : dict or sequence, a simple/nested dictionary of modifier parameters. + :param: nested_settings_path : list(stack): helps in tracing path to each node. + """ + if not isinstance(modifier_parameters, dict): + param_setting = None + for i, setting in enumerate(nested_settings_path): + + # We want to set the attribute only when we have reached the last setting. + # Applying of intermediate settings is meaningless. + if i == len(nested_settings_path) - 1: + setattr(modifier, setting, modifier_parameters) + + elif hasattr(modifier, setting): + param_setting = getattr(modifier, setting) + # getattr doesn't accept canvas_surfaces["Surface"], but we need to pass it to setattr. + if setting == "canvas_surfaces": + modifier = param_setting.active + else: + modifier = param_setting + else: + # Clean up first + bpy.ops.object.delete() + raise Exception("Modifier '{}' has no parameter named '{}'". + format(modifier_name, setting)) + + # It pops the current node before moving on to its sibling. + nested_settings_path.pop() + return + + for key in modifier_parameters: + nested_settings_path.append(key) + self._set_parameters_impl(modifier, modifier_parameters[key], nested_settings_path, modifier_name) + + if nested_settings_path: + nested_settings_path.pop() + + def set_parameters(self, modifier, modifier_parameters): + """ + Wrapper for _set_parameters_util + """ + settings = [] + modifier_name = modifier.name + self._set_parameters_impl(modifier, modifier_parameters, settings, modifier_name) + + def _add_modifier(self, test_object, modifier_spec: ModifierSpec): + """ + Add modifier to object. :param test_object: bpy.types.Object - Blender object to apply modifier on. :param modifier_spec: ModifierSpec - ModifierSpec object with parameters """ + bakers_list = ['CLOTH', 'SOFT_BODY', 'DYNAMIC_PAINT', 'FLUID'] + scene = bpy.context.scene + scene.frame_set(1) modifier = test_object.modifiers.new(modifier_spec.modifier_name, modifier_spec.modifier_type) + + if modifier is None: + raise Exception("This modifier type is already added on the Test Object, please remove it and try again.") + if self.verbose: print("Created modifier '{}' of type '{}'.". format(modifier_spec.modifier_name, modifier_spec.modifier_type)) - for param_name in modifier_spec.modifier_parameters: - try: - setattr(modifier, param_name, modifier_spec.modifier_parameters[param_name]) - if self.verbose: - print("\t set parameter '{}' with value '{}'". - format(param_name, modifier_spec.modifier_parameters[param_name])) - except AttributeError: - # Clean up first - bpy.ops.object.delete() - raise AttributeError("Modifier '{}' has no parameter named '{}'". - format(modifier_spec.modifier_type, param_name)) + # Special case for Dynamic Paint, need to toggle Canvas on. + if modifier.type == "DYNAMIC_PAINT": + bpy.ops.dpaint.type_toggle(type='CANVAS') - if self.apply_modifier: - bpy.ops.object.modifier_apply(modifier=modifier_spec.modifier_name) + self.set_parameters(modifier, modifier_spec.modifier_parameters) + + if modifier.type in bakers_list: + self._bake_current_simulation(test_object, modifier.name, modifier_spec.frame_end) + + scene.frame_set(modifier_spec.frame_end) + + def _apply_modifier(self, test_object, modifier_name): + # Modifier automatically gets applied when converting from Curve to Mesh. + if test_object.type == 'CURVE': + bpy.ops.object.convert(target='MESH') + elif test_object.type == 'MESH': + bpy.ops.object.modifier_apply(modifier=modifier_name) + else: + raise Exception("This object type is not yet supported!") + + def _bake_current_simulation(self, test_object, test_modifier_name, frame_end): + """ + FLUID: Bakes the simulation + SOFT BODY, CLOTH, DYNAMIC PAINT: Overrides the point_cache context and then bakes. + """ - def _bake_current_simulation(self, obj, test_mod_type, test_mod_name, frame_end): for scene in bpy.data.scenes: - for modifier in obj.modifiers: - if modifier.type == test_mod_type: - obj.modifiers[test_mod_name].point_cache.frame_end = frame_end - override = {'scene': scene, 'active_object': obj, 'point_cache': modifier.point_cache} + for modifier in test_object.modifiers: + if modifier.type == 'FLUID': + bpy.ops.fluid.bake_all() + break + + elif modifier.type == 'CLOTH' or modifier.type == 'SOFT_BODY': + test_object.modifiers[test_modifier_name].point_cache.frame_end = frame_end + override_setting = modifier.point_cache + override = {'scene': scene, 'active_object': test_object, 'point_cache': override_setting} + bpy.ops.ptcache.bake(override, bake=True) + break + + elif modifier.type == 'DYNAMIC_PAINT': + dynamic_paint_setting = modifier.canvas_settings.canvas_surfaces.active + override_setting = dynamic_paint_setting.point_cache + override = {'scene': scene, 'active_object': test_object, 'point_cache': override_setting} bpy.ops.ptcache.bake(override, bake=True) break - def _apply_physics_settings(self, test_object, physics_spec: PhysicsSpec): + def _apply_particle_system(self, test_object, particle_sys_spec: ParticleSystemSpec): """ - Apply Physics settings to test objects. + Applies Particle System settings to test objects """ - scene = bpy.context.scene - scene.frame_set(1) - modifier = test_object.modifiers.new(physics_spec.modifier_name, - physics_spec.modifier_type) - physics_setting = modifier.settings + bpy.context.scene.frame_set(1) + bpy.ops.object.select_all(action='DESELECT') + + test_object.modifiers.new(particle_sys_spec.modifier_name, particle_sys_spec.modifier_type) + + settings_name = test_object.particle_systems.active.settings.name + particle_setting = bpy.data.particles[settings_name] if self.verbose: print("Created modifier '{}' of type '{}'.". - format(physics_spec.modifier_name, physics_spec.modifier_type)) + format(particle_sys_spec.modifier_name, particle_sys_spec.modifier_type)) - for param_name in physics_spec.modifier_parameters: + for param_name in particle_sys_spec.modifier_parameters: try: - setattr(physics_setting, param_name, physics_spec.modifier_parameters[param_name]) + if param_name == "seed": + system_setting = test_object.particle_systems[particle_sys_spec.modifier_name] + setattr(system_setting, param_name, particle_sys_spec.modifier_parameters[param_name]) + else: + setattr(particle_setting, param_name, particle_sys_spec.modifier_parameters[param_name]) + if self.verbose: print("\t set parameter '{}' with value '{}'". - format(param_name, physics_spec.modifier_parameters[param_name])) + format(param_name, particle_sys_spec.modifier_parameters[param_name])) except AttributeError: # Clean up first bpy.ops.object.delete() raise AttributeError("Modifier '{}' has no parameter named '{}'". - format(physics_spec.modifier_type, param_name)) - - scene.frame_set(physics_spec.frame_end + 1) + format(particle_sys_spec.modifier_type, param_name)) - self._bake_current_simulation( - test_object, - physics_spec.modifier_type, - physics_spec.modifier_name, - physics_spec.frame_end, - ) + bpy.context.scene.frame_set(particle_sys_spec.frame_end) + test_object.select_set(True) + bpy.ops.object.duplicates_make_real() + test_object.select_set(True) + bpy.ops.object.join() if self.apply_modifier: - bpy.ops.object.modifier_apply(modifier=physics_spec.modifier_name) + self._apply_modifier(test_object, particle_sys_spec.modifier_name) - def _apply_operator(self, test_object, operator: OperatorSpec): + def _apply_operator_edit_mode(self, test_object, operator: OperatorSpecEditMode): """ Apply operator on test object. :param test_object: bpy.types.Object - Blender object to apply operator on. - :param operator: OperatorSpec - OperatorSpec object with parameters. + :param operator: OperatorSpecEditMode - OperatorSpecEditMode object with parameters. """ mesh = test_object.data bpy.ops.object.mode_set(mode='EDIT') @@ -340,15 +457,64 @@ class MeshTest: bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_mode(type=operator.select_mode) mesh_operator = getattr(bpy.ops.mesh, operator.operator_name) - if not mesh_operator: - raise AttributeError("No mesh operator {}".format(operator.operator_name)) - retval = mesh_operator(**operator.operator_parameters) + + try: + retval = mesh_operator(**operator.operator_parameters) + except AttributeError: + raise AttributeError("bpy.ops.mesh has no attribute {}".format(operator.operator_name)) + except TypeError as ex: + raise TypeError("Incorrect operator parameters {!r} raised {!r}".format(operator.operator_parameters, ex)) + + if retval != {'FINISHED'}: + raise RuntimeError("Unexpected operator return value: {}".format(retval)) + if self.verbose: + print("Applied {}".format(operator)) + + bpy.ops.object.mode_set(mode='OBJECT') + + def _apply_operator_object_mode(self, operator: OperatorSpecObjectMode): + """ + Applies the object operator. + """ + bpy.ops.object.mode_set(mode='OBJECT') + object_operator = getattr(bpy.ops.object, operator.operator_name) + + try: + retval = object_operator(**operator.operator_parameters) + except AttributeError: + raise AttributeError("bpy.ops.mesh has no attribute {}".format(operator.operator_name)) + except TypeError as ex: + raise TypeError("Incorrect operator parameters {!r} raised {!r}".format(operator.operator_parameters, ex)) + if retval != {'FINISHED'}: raise RuntimeError("Unexpected operator return value: {}".format(retval)) if self.verbose: print("Applied operator {}".format(operator)) + def _apply_deform_modifier(self, test_object, operation: list): + """ + param: operation: list: List of modifiers or combination of modifier and object operator. + """ + + scene = bpy.context.scene + scene.frame_set(1) bpy.ops.object.mode_set(mode='OBJECT') + modifier_operations_list = operation.modifier_list + modifier_names = [] + object_operations = operation.object_operator_spec + for modifier_operations in modifier_operations_list: + if isinstance(modifier_operations, ModifierSpec): + self._add_modifier(test_object, modifier_operations) + modifier_names.append(modifier_operations.modifier_name) + + if isinstance(object_operations, OperatorSpecObjectMode): + self._apply_operator_object_mode(object_operations) + + scene.frame_set(operation.frame_number) + + if self.apply_modifier: + for mod_name in modifier_names: + self._apply_modifier(test_object, mod_name) def run_test(self): """ @@ -369,24 +535,40 @@ class MeshTest: evaluated_test_object = bpy.context.active_object evaluated_test_object.name = "evaluated_object" if self.verbose: + print() print(evaluated_test_object.name, "is set to active") # Add modifiers and operators. for operation in self.operations_stack: if isinstance(operation, ModifierSpec): - self._apply_modifier(evaluated_test_object, operation) + self._add_modifier(evaluated_test_object, operation) + if self.apply_modifier: + self._apply_modifier(evaluated_test_object, operation.modifier_name) + + elif isinstance(operation, OperatorSpecEditMode): + self._apply_operator_edit_mode(evaluated_test_object, operation) + + elif isinstance(operation, OperatorSpecObjectMode): + self._apply_operator_object_mode(operation) - elif isinstance(operation, OperatorSpec): - self._apply_operator(evaluated_test_object, operation) + elif isinstance(operation, DeformModifierSpec): + self._apply_deform_modifier(evaluated_test_object, operation) + + elif isinstance(operation, ParticleSystemSpec): + self._apply_particle_system(evaluated_test_object, operation) - elif isinstance(operation, PhysicsSpec): - self._apply_physics_settings(evaluated_test_object, operation) else: - raise ValueError("Expected operation of type {} or {} or {}. Got {}". - format(type(ModifierSpec), type(OperatorSpec), type(PhysicsSpec), - type(operation))) + raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}". + format(type(ModifierSpec), type(OperatorSpecEditMode), + type(OperatorSpecObjectMode), type(ParticleSystemSpec), type(operation))) # Compare resulting mesh with expected one. + # Compare only when self.do_compare is set to True, it is set to False for run-test and returns. + if not self.do_compare: + print("Meshes/objects are not compared, compare evaluated and expected object in Blender for " + "visualization only.") + return False + if self.verbose: print("Comparing expected mesh with resulting mesh...") evaluated_test_mesh = evaluated_test_object.data @@ -415,75 +597,80 @@ class MeshTest: return self._on_failed_test(compare_result, validation_success, evaluated_test_object) -class OperatorTest: +class RunTest: """ - Helper class that stores and executes operator tests. + Helper class that stores and executes modifier tests. Example usage: + >>> modifier_list = [ + >>> ModifierSpec("firstSUBSURF", "SUBSURF", {"quality": 5}), + >>> ModifierSpec("firstSOLIDIFY", "SOLIDIFY", {"thickness_clamp": 0.9, "thickness": 1}) + >>> ] + >>> operator_list = [ + >>> OperatorSpecEditMode("delete_edgeloop", {}, "EDGE", MONKEY_LOOP_EDGE), + >>> ] >>> tests = [ - >>> ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_1', 'intersect_boolean', {'operation': 'UNION'}], - >>> ['FACE', {0, 1, 2, 3, 4, 5}, 'Cubecube', 'Cubecube_result_2', 'intersect_boolean', {'operation': 'INTERSECT'}], + >>> MeshTest("Test1", "testCube", "expectedCube", modifier_list), + >>> MeshTest("Test2", "testCube_2", "expectedCube_2", modifier_list), + >>> MeshTest("MonkeyDeleteEdge", "testMonkey","expectedMonkey", operator_list) >>> ] - >>> operator_test = OperatorTest(tests) - >>> operator_test.run_all_tests() + >>> modifiers_test = RunTest(tests) + >>> modifiers_test.run_all_tests() """ - def __init__(self, operator_tests): + def __init__(self, tests, apply_modifiers=False, do_compare=False): """ - Constructs an operator test. - :param operator_tests: list - list of operator test cases. Each element in the list must contain the following + Construct a modifier test. + :param tests: list - list of modifier or operator test cases. Each element in the list must contain the + following in the correct order: - 1) select_mode: str - mesh selection mode, must be either 'VERT', 'EDGE' or 'FACE' - 2) selection: set - set of vertices/edges/faces indices to select, e.g. [0, 9, 10]. - 3) test_object_name: bpy.Types.Object - test object - 4) expected_object_name: bpy.Types.Object - expected object - 5) operator_name: str - name of mesh operator from bpy.ops.mesh, e.g. "bevel" or "fill" - 6) operator_parameters: dict - {name : val} dictionary containing operator parameters. - """ - self.operator_tests = operator_tests + 0) test_name: str - unique test name + 1) test_object_name: bpy.Types.Object - test object + 2) expected_object_name: bpy.Types.Object - expected object + 3) modifiers or operators: list - list of mesh_test.ModifierSpec objects or + mesh_test.OperatorSpecEditMode objects + """ + self.tests = tests + self._ensure_unique_test_name_or_raise_error() + self.apply_modifiers = apply_modifiers + self.do_compare = do_compare self.verbose = os.environ.get("BLENDER_VERBOSE") is not None self._failed_tests_list = [] - def run_test(self, index: int): + def _ensure_unique_test_name_or_raise_error(self): """ - Run a single test from operator_tests list - :param index: int - index of test - :return: bool - True if test is successful. False otherwise. + Check if the test name is unique else raise an error. """ - case = self.operator_tests[index] - if len(case) != 6: - raise ValueError("Expected exactly 6 parameters for each test case, got {}".format(len(case))) - select_mode = case[0] - selection = case[1] - test_object_name = case[2] - expected_object_name = case[3] - operator_name = case[4] - operator_parameters = case[5] - - operator_spec = OperatorSpec(operator_name, operator_parameters, select_mode, selection) + all_test_names = [] + for each_test in self.tests: + test_name = each_test.test_name + all_test_names.append(test_name) - test = MeshTest(test_object_name, expected_object_name) - test.add_operator(operator_spec) - - success = test.run_test() - if test.is_test_updated(): - # Run the test again if the blend file has been updated. - success = test.run_test() - return success + seen_name = set() + for ele in all_test_names: + if ele in seen_name: + raise ValueError("{} is a duplicate, write a new unique name.".format(ele)) + else: + seen_name.add(ele) def run_all_tests(self): - for index, _ in enumerate(self.operator_tests): + """ + Run all tests in self.tests list. Raises an exception if one the tests fails. + """ + for test_number, each_test in enumerate(self.tests): + test_name = each_test.test_name if self.verbose: print() - print("Running test {}...".format(index)) - success = self.run_test(index) + print("Running test {}...".format(test_number)) + print("Test name {}\n".format(test_name)) + success = self.run_test(test_name) if not success: - self._failed_tests_list.append(index) + self._failed_tests_list.append(test_name) if len(self._failed_tests_list) != 0: - print("Following tests failed: {}".format(self._failed_tests_list)) + print("\nFollowing tests failed: {}".format(self._failed_tests_list)) blender_path = bpy.app.binary_path blend_path = bpy.data.filepath @@ -493,63 +680,31 @@ class OperatorTest: print("Run following command to open Blender and run the failing test:") print("{} {} --python {} -- {} {}" - .format(blender_path, blend_path, python_path, "--run-test", "<test_index>")) + .format(blender_path, blend_path, python_path, "--run-test", "<test_name>")) raise Exception("Tests {} failed".format(self._failed_tests_list)) - -class ModifierTest: - """ - Helper class that stores and executes modifier tests. - - Example usage: - - >>> modifier_list = [ - >>> ModifierSpec("firstSUBSURF", "SUBSURF", {"quality": 5}), - >>> ModifierSpec("firstSOLIDIFY", "SOLIDIFY", {"thickness_clamp": 0.9, "thickness": 1}) - >>> ] - >>> tests = [ - >>> ["testCube", "expectedCube", modifier_list], - >>> ["testCube_2", "expectedCube_2", modifier_list] - >>> ] - >>> modifiers_test = ModifierTest(tests) - >>> modifiers_test.run_all_tests() - """ - - def __init__(self, modifier_tests: list, apply_modifiers=False, threshold=None): + def run_test(self, test_name: str): """ - Construct a modifier test. - :param modifier_tests: list - list of modifier test cases. Each element in the list must contain the following - in the correct order: - 1) test_object_name: bpy.Types.Object - test object - 2) expected_object_name: bpy.Types.Object - expected object - 3) modifiers: list - list of mesh_test.ModifierSpec objects. - """ - self.modifier_tests = modifier_tests - self.apply_modifiers = apply_modifiers - self.threshold = threshold - self.verbose = os.environ.get("BLENDER_VERBOSE") is not None - self._failed_tests_list = [] - - def run_test(self, index: int): - """ - Run a single test from self.modifier_tests list - :param index: int - index of test + Run a single test from self.tests list + :param test_name: int - name of test :return: bool - True if test passed, False otherwise. """ - case = self.modifier_tests[index] - if len(case) != 3: - raise ValueError("Expected exactly 3 parameters for each test case, got {}".format(len(case))) - test_object_name = case[0] - expected_object_name = case[1] - spec_list = case[2] + case = None + for index, each_test in enumerate(self.tests): + if test_name == each_test.test_name: + case = self.tests[index] + break + + if case is None: + raise Exception('No test called {} found!'.format(test_name)) - test = MeshTest(test_object_name, expected_object_name, threshold=self.threshold) + test = case if self.apply_modifiers: test.apply_modifier = True - for modifier_spec in spec_list: - test.add_modifier(modifier_spec) + if self.do_compare: + test.do_compare = True success = test.run_test() if test.is_test_updated(): @@ -557,31 +712,3 @@ class ModifierTest: success = test.run_test() return success - - def run_all_tests(self): - """ - Run all tests in self.modifiers_tests list. Raises an exception if one the tests fails. - """ - for index, _ in enumerate(self.modifier_tests): - if self.verbose: - print() - print("Running test {}...\n".format(index)) - success = self.run_test(index) - - if not success: - self._failed_tests_list.append(index) - - if len(self._failed_tests_list) != 0: - print("Following tests failed: {}".format(self._failed_tests_list)) - - blender_path = bpy.app.binary_path - blend_path = bpy.data.filepath - frame = inspect.stack()[1] - module = inspect.getmodule(frame[0]) - python_path = module.__file__ - - print("Run following command to open Blender and run the failing test:") - print("{} {} --python {} -- {} {}" - .format(blender_path, blend_path, python_path, "--run-test", "<test_index>")) - - raise Exception("Tests {} failed".format(self._failed_tests_list)) diff --git a/tests/python/operators.py b/tests/python/operators.py index 901820c7b2d..93cdfebab7b 100644 --- a/tests/python/operators.py +++ b/tests/python/operators.py @@ -26,7 +26,7 @@ from random import shuffle, seed seed(0) sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import OperatorTest, OperatorSpec +from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest # Central vertical loop of Suzanne MONKEY_LOOP_VERT = {68, 69, 71, 73, 74, 75, 76, 77, 90, 129, 136, 175, 188, 189, 198, 207, @@ -39,126 +39,181 @@ def main(): tests = [ #### 0 # bisect - ['FACE', {0, 1, 2, 3, 4, 5}, "testCubeBisect", "expectedCubeBisect", "bisect", - {"plane_co": (0, 0, 0), "plane_no": (0, 1, 1), "clear_inner": True, "use_fill": True}], + MeshTest("CubeBisect", "testCubeBisect", "expectedCubeBisect", + [OperatorSpecEditMode("bisect", + {"plane_co": (0, 0, 0), "plane_no": (0, 1, 1), "clear_inner": True, + "use_fill": True}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), # blend from shape - ['FACE', {0, 1, 2, 3, 4, 5}, "testCubeBlendFromShape", "expectedCubeBlendFromShape", "blend_from_shape", - {"shape": "Key 1"}], + MeshTest("CubeBlendFromShape", "testCubeBlendFromShape", "expectedCubeBlendFromShape", + [OperatorSpecEditMode("blend_from_shape", {"shape": "Key 1"}, 'FACE', {0, 1, 2, 3, 4, 5})]), # bridge edge loops - ["FACE", {0, 1}, "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop", "bridge_edge_loops", {}], + MeshTest("CubeBridgeEdgeLoop", "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop", + [OperatorSpecEditMode("bridge_edge_loops", {}, "FACE", {0, 1})]), # decimate - ["FACE", {i for i in range(500)}, "testMonkeyDecimate", "expectedMonkeyDecimate", "decimate", {"ratio": 0.1}], + MeshTest("MonkeyDecimate", "testMonkeyDecimate", "expectedMonkeyDecimate", + [OperatorSpecEditMode("decimate", + {"ratio": 0.1}, "FACE", {i for i in range(500)})]), ### 4 # delete - ["VERT", {3}, "testCubeDeleteVertices", "expectedCubeDeleteVertices", "delete", {}], - ["FACE", {0}, "testCubeDeleteFaces", "expectedCubeDeleteFaces", "delete", {}], - ["EDGE", {0, 1, 2, 3}, "testCubeDeleteEdges", "expectedCubeDeleteEdges", "delete", {}], + MeshTest("CubeDeleteVertices", "testCubeDeleteVertices", "expectedCubeDeleteVertices", + [OperatorSpecEditMode("delete", {}, "VERT", {3})]), + MeshTest("CubeDeleteFaces", "testCubeDeleteFaces", "expectedCubeDeleteFaces", + [OperatorSpecEditMode("delete", {}, "FACE", {0})]), + MeshTest("CubeDeleteEdges", "testCubeDeleteEdges", "expectedCubeDeleteEdges", + [OperatorSpecEditMode("delete", {}, "EDGE", {0, 1, 2, 3})]), # delete edge loop - ["VERT", MONKEY_LOOP_VERT, "testMokneyDeleteEdgeLoopVertices", "expectedMonkeyDeleteEdgeLoopVertices", - "delete_edgeloop", {}], - ["EDGE", MONKEY_LOOP_EDGE, "testMokneyDeleteEdgeLoopEdges", "expectedMonkeyDeleteEdgeLoopEdges", - "delete_edgeloop", {}], + MeshTest("MonkeyDeleteEdgeLoopVertices", "testMokneyDeleteEdgeLoopVertices", + "expectedMonkeyDeleteEdgeLoopVertices", + [OperatorSpecEditMode("delete_edgeloop", {}, "VERT", MONKEY_LOOP_VERT)]), + + MeshTest("MonkeyDeleteEdgeLoopEdges", "testMokneyDeleteEdgeLoopEdges", + "expectedMonkeyDeleteEdgeLoopEdges", + [OperatorSpecEditMode("delete_edgeloop", {}, "EDGE", MONKEY_LOOP_EDGE)]), ### 9 # delete loose - ["VERT", {i for i in range(12)}, "testCubeDeleteLooseVertices", "expectedCubeDeleteLooseVertices", - "delete_loose", {"use_verts": True, "use_edges": False, "use_faces": False}], - ["EDGE", {i for i in range(14)}, "testCubeDeleteLooseEdges", "expectedCubeDeleteLooseEdges", - "delete_loose", {"use_verts": False, "use_edges": True, "use_faces": False}], - ["FACE", {i for i in range(7)}, "testCubeDeleteLooseFaces", "expectedCubeDeleteLooseFaces", - "delete_loose", {"use_verts": False, "use_edges": False, "use_faces": True}], + MeshTest("CubeDeleteLooseVertices", "testCubeDeleteLooseVertices", + "expectedCubeDeleteLooseVertices", + [OperatorSpecEditMode("delete_loose", {"use_verts": True, "use_edges": False, "use_faces": False}, + "VERT", + {i for i in range(12)})]), + MeshTest("CubeDeleteLooseEdges", "testCubeDeleteLooseEdges", + "expectedCubeDeleteLooseEdges", + [OperatorSpecEditMode("delete_loose", {"use_verts": False, "use_edges": True, "use_faces": False}, + "EDGE", + {i for i in range(14)})]), + MeshTest("CubeDeleteLooseFaces", "testCubeDeleteLooseFaces", + "expectedCubeDeleteLooseFaces", + [OperatorSpecEditMode("delete_loose", {"use_verts": False, "use_edges": False, "use_faces": True}, + "FACE", + {i for i in range(7)})]), # dissolve degenerate - ["VERT", {i for i in range(8)}, "testCubeDissolveDegenerate", "expectedCubeDissolveDegenerate", - "dissolve_degenerate", {}], + MeshTest("CubeDissolveDegenerate", "testCubeDissolveDegenerate", + "expectedCubeDissolveDegenerate", + [OperatorSpecEditMode("dissolve_degenerate", {}, "VERT", {i for i in range(8)})]), ### 13 # dissolve edges - ["EDGE", {0, 5, 6, 9}, "testCylinderDissolveEdges", "expectedCylinderDissolveEdges", - "dissolve_edges", {}], + MeshTest("CylinderDissolveEdges", "testCylinderDissolveEdges", "expectedCylinderDissolveEdges", + [OperatorSpecEditMode("dissolve_edges", {}, "EDGE", {0, 5, 6, 9})]), # dissolve faces - ["VERT", {5, 34, 47, 49, 83, 91, 95}, "testCubeDissolveFaces", "expectedCubeDissolveFaces", "dissolve_faces", - {}], + MeshTest("CubeDissolveFaces", "testCubeDissolveFaces", "expectedCubeDissolveFaces", + [OperatorSpecEditMode("dissolve_faces", {}, "VERT", {5, 34, 47, 49, 83, 91, 95})]), ### 15 # dissolve verts - ["VERT", {16, 20, 22, 23, 25}, "testCubeDissolveVerts", "expectedCubeDissolveVerts", "dissolve_verts", {}], + MeshTest("CubeDissolveVerts", "testCubeDissolveVerts", "expectedCubeDissolveVerts", + [OperatorSpecEditMode("dissolve_verts", {}, "VERT", {16, 20, 22, 23, 25})]), # duplicate - ["VERT", {i for i in range(33)} - {23}, "testConeDuplicateVertices", "expectedConeDuplicateVertices", - "duplicate", {}], - ["VERT", {23}, "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex", "duplicate", {}], - ["FACE", {6, 9}, "testConeDuplicateFaces", "expectedConeDuplicateFaces", "duplicate", {}], - ["EDGE", {i for i in range(64)}, "testConeDuplicateEdges", "expectedConeDuplicateEdges", "duplicate", {}], + MeshTest("ConeDuplicateVertices", "testConeDuplicateVertices", + "expectedConeDuplicateVertices", + [OperatorSpecEditMode("duplicate", {}, "VERT", {i for i in range(33)} - {23})]), + + MeshTest("ConeDuplicateOneVertex", "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex", + [OperatorSpecEditMode("duplicate", {}, "VERT", {23})]), + MeshTest("ConeDuplicateFaces", "testConeDuplicateFaces", "expectedConeDuplicateFaces", + [OperatorSpecEditMode("duplicate", {}, "FACE", {6, 9})]), + MeshTest("ConeDuplicateEdges", "testConeDuplicateEdges", "expectedConeDuplicateEdges", + [OperatorSpecEditMode("duplicate", {}, "EDGE", {i for i in range(64)})]), ### 20 # edge collapse - ["EDGE", {1, 9, 4}, "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse", "edge_collapse", {}], + MeshTest("CylinderEdgeCollapse", "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse", + [OperatorSpecEditMode("edge_collapse", {}, "EDGE", {1, 9, 4})]), # edge face add - ["VERT", {1, 3, 4, 5, 7}, "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace", "edge_face_add", {}], - ["VERT", {4, 5}, "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge", "edge_face_add", {}], + MeshTest("CubeEdgeFaceAddFace", "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace", + [OperatorSpecEditMode("edge_face_add", {}, "VERT", {1, 3, 4, 5, 7})]), + MeshTest("CubeEdgeFaceAddEdge", "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge", + [OperatorSpecEditMode("edge_face_add", {}, "VERT", {4, 5})]), # edge rotate - ["EDGE", {1}, "testCubeEdgeRotate", "expectedCubeEdgeRotate", "edge_rotate", {}], + MeshTest("CubeEdgeRotate", "testCubeEdgeRotate", "expectedCubeEdgeRotate", + [OperatorSpecEditMode("edge_rotate", {}, "EDGE", {1})]), # edge split - ["EDGE", {2, 5, 8, 11, 14, 17, 20, 23}, "testCubeEdgeSplit", "expectedCubeEdgeSplit", "edge_split", {}], + MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit", + [OperatorSpecEditMode("edge_split", {}, "EDGE", {2, 5, 8, 11, 14, 17, 20, 23})]), ### 25 # face make planar - ["FACE", {i for i in range(500)}, "testMonkeyFaceMakePlanar", "expectedMonkeyFaceMakePlanar", - "face_make_planar", {}], + MeshTest("MonkeyFaceMakePlanar", "testMonkeyFaceMakePlanar", + "expectedMonkeyFaceMakePlanar", + [OperatorSpecEditMode("face_make_planar", {}, "FACE", {i for i in range(500)})]), # face split by edges - ["VERT", {i for i in range(6)}, "testPlaneFaceSplitByEdges", "expectedPlaneFaceSplitByEdges", - "face_split_by_edges", {}], + MeshTest("PlaneFaceSplitByEdges", "testPlaneFaceSplitByEdges", + "expectedPlaneFaceSplitByEdges", + [OperatorSpecEditMode("face_split_by_edges", {}, "VERT", {i for i in range(6)})]), # fill - ["EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49}, "testIcosphereFill", "expectedIcosphereFill", - "fill", {}], - ["EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49}, "testIcosphereFillUseBeautyFalse", - "expectedIcosphereFillUseBeautyFalse", "fill", {"use_beauty": False}], + MeshTest("IcosphereFill", "testIcosphereFill", "expectedIcosphereFill", + [OperatorSpecEditMode("fill", {}, "EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49})]), + MeshTest("IcosphereFillUseBeautyFalse", + "testIcosphereFillUseBeautyFalse", "expectedIcosphereFillUseBeautyFalse", + [OperatorSpecEditMode("fill", {"use_beauty": False}, "EDGE", + {20, 21, 22, 23, 24, 45, 46, 47, 48, 49})]), # fill grid - ["EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15}, "testPlaneFillGrid", "expectedPlaneFillGrid", - "fill_grid", {}], - ["EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15}, "testPlaneFillGridSimpleBlending", - "expectedPlaneFillGridSimpleBlending", "fill_grid", {"use_interp_simple": True}], + MeshTest("PlaneFillGrid", "testPlaneFillGrid", + "expectedPlaneFillGrid", + [OperatorSpecEditMode("fill_grid", {}, "EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15})]), + + MeshTest("PlaneFillGridSimpleBlending", + "testPlaneFillGridSimpleBlending", + "expectedPlaneFillGridSimpleBlending", + [OperatorSpecEditMode("fill_grid", {"use_interp_simple": True}, "EDGE", + {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15})]), ### 31 # fill holes - ["VERT", {i for i in range(481)}, "testSphereFillHoles", "expectedSphereFillHoles", "fill_holes", {"sides": 9}], + MeshTest("SphereFillHoles", "testSphereFillHoles", "expectedSphereFillHoles", + [OperatorSpecEditMode("fill_holes", {"sides": 9}, "VERT", {i for i in range(481)})]), # inset faces - ["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95}, "testCubeInset", - "expectedCubeInset", "inset", {"thickness": 0.2}], - ["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95}, - "testCubeInsetEvenOffsetFalse", "expectedCubeInsetEvenOffsetFalse", - "inset", {"thickness": 0.2, "use_even_offset": False}], - ["VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95}, "testCubeInsetDepth", - "expectedCubeInsetDepth", "inset", {"thickness": 0.2, "depth": 0.2}], - ["FACE", {35, 36, 37, 45, 46, 47, 55, 56, 57}, "testGridInsetRelativeOffset", "expectedGridInsetRelativeOffset", - "inset", {"thickness": 0.4, "use_relative_offset": True}], + MeshTest("CubeInset", + "testCubeInset", "expectedCubeInset", [OperatorSpecEditMode("inset", {"thickness": 0.2}, "VERT", + {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, + 52, + 59, 61, 62, 65, 83, 91, 95})]), + + MeshTest("CubeInsetEvenOffsetFalse", + "testCubeInsetEvenOffsetFalse", "expectedCubeInsetEvenOffsetFalse", + [OperatorSpecEditMode("inset", {"thickness": 0.2, "use_even_offset": False}, "VERT", + {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95})]), + MeshTest("CubeInsetDepth", + "testCubeInsetDepth", + "expectedCubeInsetDepth", [OperatorSpecEditMode("inset", {"thickness": 0.2, "depth": 0.2}, "VERT", + {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, + 62, + 65, 83, 91, 95})]), + MeshTest("GridInsetRelativeOffset", "testGridInsetRelativeOffset", + "expectedGridInsetRelativeOffset", + [OperatorSpecEditMode("inset", {"thickness": 0.4, + "use_relative_offset": True}, "FACE", + {35, 36, 37, 45, 46, 47, 55, 56, 57})]), ] - operators_test = OperatorTest(tests) + operators_test = RunTest(tests) command = list(sys.argv) for i, cmd in enumerate(command): if cmd == "--run-all-tests": + operators_test.do_compare = True operators_test.run_all_tests() break elif cmd == "--run-test": - operators_test.apply_modifiers = False - index = int(command[i + 1]) - operators_test.run_test(index) + operators_test.do_compare = False + name = command[i + 1] + operators_test.run_test(name) break diff --git a/tests/python/physics_cloth.py b/tests/python/physics_cloth.py index 5b9151ea089..b88b4d63f9d 100644 --- a/tests/python/physics_cloth.py +++ b/tests/python/physics_cloth.py @@ -24,26 +24,42 @@ import sys import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import ModifierTest, PhysicsSpec +from modules.mesh_test import RunTest, ModifierSpec, MeshTest def main(): test = [ - ["testCloth", "expectedCloth", - [PhysicsSpec('Cloth', 'CLOTH', {'quality': 5}, 35)]], + + MeshTest("ClothSimple", "testClothPlane", "expectedClothPlane", + [ModifierSpec('Cloth', 'CLOTH', {'settings': {'quality': 5}}, 15)], threshold=1e-3), + + # Not reproducible + # MeshTest("ClothPressure", "testObjClothPressure", "expObjClothPressure", + # [ModifierSpec('Cloth2', 'CLOTH', {'settings': {'use_pressure': True, + # 'uniform_pressure_force': 1}}, 16)]), + + # Not reproducible + # MeshTest("ClothSelfCollision", "testClothCollision", "expClothCollision", + # [ModifierSpec('Cloth', 'CLOTH', {'collision_settings': {'use_self_collision': True}}, 67)]), + + MeshTest("ClothSpring", "testTorusClothSpring", "expTorusClothSpring", + [ModifierSpec('Cloth2', 'CLOTH', {'settings': {'use_internal_springs': True}}, 10)], threshold=1e-3), + ] - cloth_test = ModifierTest(test, threshold=1e-3) + cloth_test = RunTest(test) command = list(sys.argv) for i, cmd in enumerate(command): if cmd == "--run-all-tests": cloth_test.apply_modifiers = True + cloth_test.do_compare = True cloth_test.run_all_tests() break elif cmd == "--run-test": cloth_test.apply_modifiers = False - index = int(command[i + 1]) - cloth_test.run_test(index) + cloth_test.do_compare = False + name = command[i + 1] + cloth_test.run_test(name) break diff --git a/tests/python/physics_dynamic_paint.py b/tests/python/physics_dynamic_paint.py new file mode 100644 index 00000000000..b5d09c8cb3a --- /dev/null +++ b/tests/python/physics_dynamic_paint.py @@ -0,0 +1,58 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import os +import sys + +import bpy + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import RunTest, ModifierSpec, MeshTest + + +def main(): + test = [ + + MeshTest("DynamicPaintSimple", "testObjDynamicPaintPlane", "expObjDynamicPaintPlane", + [ModifierSpec('dynamic_paint', 'DYNAMIC_PAINT', + {'ui_type': 'CANVAS', + 'canvas_settings': {'canvas_surfaces': {'surface_type': 'WAVE', 'frame_end': 15}}}, + 15)]), + + ] + dynamic_paint_test = RunTest(test) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + dynamic_paint_test.apply_modifiers = True + dynamic_paint_test.do_compare = True + dynamic_paint_test.run_all_tests() + break + elif cmd == "--run-test": + dynamic_paint_test.apply_modifiers = False + dynamic_paint_test.do_compare = False + name = command[i + 1] + dynamic_paint_test.run_test(name) + break + + +if __name__ == "__main__": + main() diff --git a/tests/python/physics_ocean.py b/tests/python/physics_ocean.py new file mode 100644 index 00000000000..40227d3d8d7 --- /dev/null +++ b/tests/python/physics_ocean.py @@ -0,0 +1,54 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import os +import sys + +import bpy + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import RunTest, ModifierSpec, MeshTest + + +def main(): + test = [ + # World coordinates of test and expected object should be same. + MeshTest("PlaneOcean", "testObjPlaneOcean", "expObjPlaneOcean", + [ModifierSpec('Ocean', 'OCEAN', {})]), + ] + ocean_test = RunTest(test) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + ocean_test.apply_modifiers = True + ocean_test.do_compare = True + ocean_test.run_all_tests() + break + elif cmd == "--run-test": + ocean_test.apply_modifiers = False + ocean_test.do_compare = False + name = command[i + 1] + ocean_test.run_test(name) + break + + +if __name__ == "__main__": + main() diff --git a/tests/python/physics_particle_instance.py b/tests/python/physics_particle_instance.py new file mode 100644 index 00000000000..e12e357e4ce --- /dev/null +++ b/tests/python/physics_particle_instance.py @@ -0,0 +1,56 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import os +import sys + +import bpy + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import RunTest, ModifierSpec, MeshTest + + +def main(): + test = [ + + MeshTest("ParticleInstanceSimple", "testParticleInstance", "expectedParticleInstance", + [ModifierSpec('ParticleInstance', 'PARTICLE_INSTANCE', {'object': bpy.data.objects['Cube']})], + threshold=1e-3), + + ] + particle_instance_test = RunTest(test) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + particle_instance_test.apply_modifiers = True + particle_instance_test.do_compare = True + particle_instance_test.run_all_tests() + break + elif cmd == "--run-test": + particle_instance_test.apply_modifiers = False + particle_instance_test.do_compare = False + name = command[i + 1] + particle_instance_test.run_test(name) + break + + +if __name__ == "__main__": + main() diff --git a/tests/python/physics_particle_system.py b/tests/python/physics_particle_system.py new file mode 100644 index 00000000000..0adc5ab1c54 --- /dev/null +++ b/tests/python/physics_particle_system.py @@ -0,0 +1,55 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import os +import sys + +import bpy + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import RunTest, ParticleSystemSpec, MeshTest + + +def main(): + test = [ + MeshTest("ParticleSystemTest", "testParticleSystem", "expParticleSystem", + [ParticleSystemSpec('Particles', 'PARTICLE_SYSTEM', {'render_type': "OBJECT", + 'instance_object': bpy.data.objects['Cube']}, 20)], threshold=1e-3), + + ] + particle_test = RunTest(test) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + particle_test.apply_modifiers = True + particle_test.do_compare = True + particle_test.run_all_tests() + break + elif cmd == "--run-test": + particle_test.apply_modifiers = False + particle_test.do_compare = False + name = command[i + 1] + particle_test.run_test(name) + break + + +if __name__ == "__main__": + main() diff --git a/tests/python/physics_softbody.py b/tests/python/physics_softbody.py index 8d431be742c..985b3a29bb4 100644 --- a/tests/python/physics_softbody.py +++ b/tests/python/physics_softbody.py @@ -24,26 +24,31 @@ import sys import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import ModifierTest, PhysicsSpec +from modules.mesh_test import RunTest, ModifierSpec, MeshTest def main(): test = [ - ["testSoftBody", "expectedSoftBody", - [PhysicsSpec('Softbody', 'SOFT_BODY', {'use_goal': False, 'bend': 8, 'pull': 0.8, 'push': 0.8}, 45)]], + + MeshTest("SoftBodySimple", "testSoftBody", "expectedSoftBody", + [ModifierSpec('Softbody', 'SOFT_BODY', + {'settings': {'use_goal': False, 'bend': 8, 'pull': 0.8, 'push': 0.8}}, + 45)]), ] - softBody_test = ModifierTest(test) + soft_body_test = RunTest(test) command = list(sys.argv) for i, cmd in enumerate(command): if cmd == "--run-all-tests": - softBody_test.apply_modifiers = True - softBody_test.run_all_tests() + soft_body_test.apply_modifiers = True + soft_body_test.do_compare = True + soft_body_test.run_all_tests() break elif cmd == "--run-test": - softBody_test.apply_modifiers = False - index = int(command[i + 1]) - softBody_test.run_test(index) + soft_body_test.apply_modifiers = False + soft_body_test.do_compare = False + name = command[i + 1] + soft_body_test.run_test(name) break |