Merge branch 'master' into sculpt-dev

This commit is contained in:
Pablo Dobarro 2021-05-27 11:24:32 +02:00
commit 702376e935
395 changed files with 9271 additions and 3304 deletions

View File

@ -29,7 +29,7 @@ set(BLOSC_EXTRA_ARGS
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
)
# Prevent blosc from including it's own local copy of zlib in the object file
# Prevent blosc from including its own local copy of zlib in the object file
# and cause linker errors with everybody else.
set(BLOSC_EXTRA_ARGS ${BLOSC_EXTRA_ARGS}
-DPREFER_EXTERNAL_ZLIB=ON

View File

@ -43,7 +43,7 @@ set(JPEG_FILE libjpeg-turbo-${JPEG_VERSION}.tar.gz)
set(BOOST_VERSION 1.73.0)
set(BOOST_VERSION_NODOTS 1_73_0)
set(BOOST_VERSION_NODOTS_SHORT 1_73)
set(BOOST_URI https://dl.bintray.com/boostorg/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_NODOTS}.tar.gz)
set(BOOST_URI https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_NODOTS}.tar.gz)
set(BOOST_HASH 4036cd27ef7548b8d29c30ea10956196)
set(BOOST_HASH_TYPE MD5)
set(BOOST_FILE boost_${BOOST_VERSION_NODOTS}.tar.gz)
@ -297,10 +297,10 @@ set(OPENJPEG_HASH 63f5a4713ecafc86de51bfad89cc07bb788e9bba24ebbf0c4ca637621aadb6
set(OPENJPEG_HASH_TYPE SHA256)
set(OPENJPEG_FILE openjpeg-v${OPENJPEG_VERSION}.tar.gz)
set(FFMPEG_VERSION 4.2.3)
set(FFMPEG_VERSION 4.4)
set(FFMPEG_URI http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2)
set(FFMPEG_HASH 695fad11f3baf27784e24cb0e977b65a)
set(FFMPEG_HASH_TYPE MD5)
set(FFMPEG_HASH 42093549751b582cf0f338a21a3664f52e0a9fbe0d238d3c992005e493607d0e)
set(FFMPEG_HASH_TYPE SHA256)
set(FFMPEG_FILE ffmpeg-${FFMPEG_VERSION}.tar.bz2)
set(FFTW_VERSION 3.3.8)

View File

@ -37,7 +37,7 @@ if [ $USE_DEBUG_TRAP -ne 0 ]; then
trap 'err_report $LINENO' ERR
fi
# Noisy, show every line that runs with it's line number.
# Noisy, show every line that runs with its line number.
if [ $USE_DEBUG_LOG -ne 0 ]; then
PS4='\e[0;33m$(printf %4d ${LINENO}):\e\033[0m '
set -x
@ -563,9 +563,9 @@ OIDN_SKIP=false
ISPC_VERSION="1.14.1"
FFMPEG_VERSION="4.2.3"
FFMPEG_VERSION_SHORT="4.2"
FFMPEG_VERSION_MIN="3.0"
FFMPEG_VERSION="4.4"
FFMPEG_VERSION_SHORT="4.4"
FFMPEG_VERSION_MIN="4.4"
FFMPEG_VERSION_MAX="5.0"
FFMPEG_FORCE_BUILD=false
FFMPEG_FORCE_REBUILD=false

View File

@ -20,7 +20,7 @@
# ILMBASE_LIBRARIES - list of libraries to link against when using IlmBase.
# ILMBASE_FOUND - True if IlmBase was found.
# Other standarnd issue macros
# Other standard issue macros
include(FindPackageHandleStandardArgs)
include(FindPackageMessage)
include(SelectLibraryConfigurations)

View File

@ -22,7 +22,7 @@
# These are defined by the FindIlmBase module.
# OPENEXR_FOUND - True if OpenEXR was found.
# Other standarnd issue macros
# Other standard issue macros
include(SelectLibraryConfigurations)
include(FindPackageHandleStandardArgs)
include(FindPackageMessage)

View File

@ -79,7 +79,7 @@ if(EXISTS ${SOURCE_DIR}/.git)
ERROR_QUIET)
if(NOT _git_below_check STREQUAL "")
# If there're commits between HEAD and upstream this means
# that we're reset-ed to older revision. Use it's hash then.
# that we're reset-ed to older revision. Use its hash then.
execute_process(COMMAND git rev-parse --short=12 HEAD
WORKING_DIRECTORY ${SOURCE_DIR}
OUTPUT_VARIABLE MY_WC_HASH

View File

@ -104,8 +104,8 @@ if(WIN32)
set(CPACK_WIX_LIGHT_EXTRA_FLAGS -dcl:medium)
endif()
set(CPACK_PACKAGE_EXECUTABLES "blender" "blender")
set(CPACK_CREATE_DESKTOP_LINKS "blender" "blender")
set(CPACK_PACKAGE_EXECUTABLES "blender-launcher" "blender")
set(CPACK_CREATE_DESKTOP_LINKS "blender-launcher" "blender")
include(CPack)

View File

@ -20,12 +20,6 @@
# Libraries configuration for Apple.
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
set(MACOSX_DEPLOYMENT_TARGET 11.00)
else()
set(MACOSX_DEPLOYMENT_TARGET 10.13)
endif()
macro(find_package_wrapper)
# do nothing, just satisfy the macro
endmacro()

View File

@ -168,21 +168,15 @@ endif()
unset(OSX_SDKROOT)
# 10.13 is our min. target, if you use higher sdk, weak linking happens
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
# M1 chips run Big Sur onwards.
set(OSX_MIN_DEPLOYMENT_TARGET 11.00)
else()
# 10.13 is our min. target, if you use higher sdk, weak linking happens
set(OSX_MIN_DEPLOYMENT_TARGET 10.13)
endif()
if(CMAKE_OSX_DEPLOYMENT_TARGET)
if(${CMAKE_OSX_DEPLOYMENT_TARGET} VERSION_LESS ${OSX_MIN_DEPLOYMENT_TARGET})
message(STATUS "Setting deployment target to ${OSX_MIN_DEPLOYMENT_TARGET}, lower versions are not supported")
set(CMAKE_OSX_DEPLOYMENT_TARGET "${OSX_MIN_DEPLOYMENT_TARGET}" CACHE STRING "" FORCE)
endif()
else()
set(CMAKE_OSX_DEPLOYMENT_TARGET "${OSX_MIN_DEPLOYMENT_TARGET}" CACHE STRING "" FORCE)
endif()
set(CMAKE_OSX_DEPLOYMENT_TARGET "${OSX_MIN_DEPLOYMENT_TARGET}" CACHE STRING "" FORCE)
if(NOT ${CMAKE_GENERATOR} MATCHES "Xcode")
# Force CMAKE_OSX_DEPLOYMENT_TARGET for makefiles, will not work else (CMake bug?)

View File

@ -29,10 +29,10 @@
#if OPENVDB == 1
# include "openvdb/openvdb.h"
# include <openvdb/points/PointConversion.h>
# include <openvdb/points/PointCount.h>
# include <openvdb/tools/Clip.h>
# include <openvdb/tools/Dense.h>
# include "openvdb/points/PointConversion.h"
# include "openvdb/points/PointCount.h"
# include "openvdb/tools/Clip.h"
# include "openvdb/tools/Dense.h"
#endif
#define POSITION_NAME "P"
@ -519,7 +519,7 @@ int writeObjectsVDB(const string &filename,
}
}
// Write only if the is at least one grid, optionally write with compression.
// Write only if there is at least one grid, optionally write with compression.
if (gridsVDB.size()) {
int vdb_flags = openvdb::io::COMPRESS_ACTIVE_MASK;
switch (compression) {
@ -534,7 +534,8 @@ int writeObjectsVDB(const string &filename,
}
case COMPRESSION_BLOSC: {
# if OPENVDB_BLOSC == 1
vdb_flags |= openvdb::io::COMPRESS_BLOSC;
// Cannot use |= here, causes segfault with blosc 1.5.0 (== recommended version)
vdb_flags = openvdb::io::COMPRESS_BLOSC;
# else
debMsg("OpenVDB was built without Blosc support, using Zip compression instead", 1);
vdb_flags |= openvdb::io::COMPRESS_ZIP;

View File

@ -384,6 +384,7 @@ class FluidSolver : public PbClass {
GridStorage<Real> mGrids4dReal;
GridStorage<Vec3> mGrids4dVec;
GridStorage<Vec4> mGrids4dVec4;
public:
PbArgs _args;
}

View File

@ -42,7 +42,7 @@ inline void updateQtGui(bool full, int frame, float time, const std::string &cur
# ifdef _DEBUG
# define DEBUG 1
# endif // _DEBUG
#endif // DEBUG
#endif // DEBUG
// Standard exception
class Error : public std::exception {

View File

@ -1,3 +1,3 @@
#define MANTA_GIT_VERSION "commit 39b7a415721ecbf6643612a24e8eadd221aeb934"
#define MANTA_GIT_VERSION "commit 9c505cd22e289b98c9aa717efba8ef3201c7e458"

View File

@ -389,6 +389,7 @@ class GridBase : public PbClass {
Real mDx;
bool m3D; // precomputed Z shift: to ensure 2D compatibility, always use this instead of sx*sy !
IndexInt mStrideZ;
public:
PbArgs _args;
}

View File

@ -326,6 +326,7 @@ class Grid4dBase : public PbClass {
// precomputed Z,T shift: to ensure 2D compatibility, always use this instead of sx*sy !
IndexInt mStrideZ;
IndexInt mStrideT;
public:
PbArgs _args;
}
@ -950,6 +951,7 @@ template<class T> class Grid4d : public Grid4dBase {
protected:
T *mData;
public:
PbArgs _args;
}

View File

@ -266,6 +266,7 @@ class LevelsetGrid : public Grid<Real> {
}
static Real invalidTimeValue();
public:
PbArgs _args;
}

View File

@ -796,6 +796,7 @@ class Mesh : public PbClass {
std::vector<MeshDataImpl<int> *>
mMdataInt; //! indicate that mdata of this mesh is copied, and needs to be freed
bool mFreeMdata;
public:
PbArgs _args;
}
@ -881,6 +882,7 @@ class MeshDataBase : public PbClass {
protected:
Mesh *mMesh;
public:
PbArgs _args;
}
@ -1645,6 +1647,7 @@ template<class T> class MeshDataImpl : public MeshDataBase {
//! optionally , we might have an associated grid from which to grab new data
Grid<T> *mpGridSource; //! unfortunately , we need to distinguish mac vs regular vec3
bool mGridSourceMAC;
public:
PbArgs _args;
}

View File

@ -154,6 +154,7 @@ class MovingObstacle : public PbClass {
int mEmptyType;
int mID;
static int sIDcnt;
public:
PbArgs _args;
}

View File

@ -236,6 +236,7 @@ class WaveletNoiseField : public PbClass {
static int randomSeed;
// global reference count for noise tile
static std::atomic<int> mNoiseReferenceCount;
public:
PbArgs _args;
}

View File

@ -205,6 +205,7 @@ class ParticleBase : public PbClass {
//! custom seed for particle systems, used by plugins
int mSeed; //! fix global random seed storage, used mainly by functions in this class
static int globalSeed;
public:
PbArgs _args;
}
@ -628,6 +629,7 @@ template<class S> class ParticleSystem : public ParticleBase {
std::vector<S> mData;
//! reduce storage , called by doCompress
virtual void compress();
public:
PbArgs _args;
}
@ -918,6 +920,7 @@ class ParticleIndexSystem : public ParticleSystem<ParticleIndexData> {
return -1;
}
};
public:
PbArgs _args;
}
@ -982,6 +985,7 @@ template<class DATA, class CON> class ConnectedParticleSystem : public ParticleS
protected:
std::vector<CON> mSegments;
virtual void compress();
public:
PbArgs _args;
}
@ -1071,6 +1075,7 @@ class ParticleDataBase : public PbClass {
protected:
ParticleBase *mpParticleSys;
public:
PbArgs _args;
}
@ -1843,6 +1848,7 @@ template<class T> class ParticleDataImpl : public ParticleDataBase {
//! optionally , we might have an associated grid from which to grab new data
Grid<T> *mpGridSource; //! unfortunately , we need to distinguish mac vs regular vec3
bool mGridSourceMAC;
public:
PbArgs _args;
}

View File

@ -234,10 +234,10 @@ void subdivideMesh(
normalize(ne2);
// Real thisArea = sqrMag(cross(-e2,e0));
// small angle approximation says sin(x) = arcsin(x) = x,
// arccos(x) = pi/2 - arcsin(x),
// cos(x) = dot(A,B),
// so angle is approximately 1 - dot(A,B).
// small angle approximation says sin(x) = arcsin(x) = x,
// arccos(x) = pi/2 - arcsin(x),
// cos(x) = dot(A,B),
// so angle is approximately 1 - dot(A,B).
Real angle[3];
angle[0] = 1.0 - dot(ne0, -ne2);
angle[1] = 1.0 - dot(ne1, -ne0);

View File

@ -2287,9 +2287,10 @@ struct knFlipComputePotentialTrappedAir : public KernelBase {
const Vec3 &vj = scaleFromManta * v.getCentered(x, y, z);
const Vec3 xij = xi - xj;
const Vec3 vij = vi - vj;
Real h = !pot.is3D() ? 1.414 * radius :
1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius
// for h, due to squared resp. cubic neighbor area
Real h = !pot.is3D() ?
1.414 * radius :
1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius for h, due
// to squared resp. cubic neighbor area
vdiff += norm(vij) * (1 - dot(getNormalized(vij), getNormalized(xij))) *
(1 - norm(xij) / h);
}

View File

@ -269,6 +269,7 @@ class Shape : public PbClass {
protected:
GridType mType;
public:
PbArgs _args;
}
@ -319,6 +320,7 @@ class NullShape : public Shape {
{
gridSetConst<Real>(phi, 1000.0f);
}
public:
PbArgs _args;
}
@ -394,6 +396,7 @@ class Box : public Shape {
protected:
Vec3 mP0, mP1;
public:
PbArgs _args;
}
@ -455,6 +458,7 @@ class Sphere : public Shape {
protected:
Vec3 mCenter, mScale;
Real mRadius;
public:
PbArgs _args;
}
@ -579,6 +583,7 @@ class Cylinder : public Shape {
protected:
Vec3 mCenter, mZDir;
Real mRadius, mZ;
public:
PbArgs _args;
}
@ -655,6 +660,7 @@ class Slope : public Shape {
Real mAnglexy, mAngleyz;
Real mOrigin;
Vec3 mGs;
public:
PbArgs _args;
}

View File

@ -199,6 +199,7 @@ class TurbulenceParticleSystem : public ParticleSystem<TurbulenceParticleData> {
private:
WaveletNoiseField &noise;
public:
PbArgs _args;
}

View File

@ -127,6 +127,7 @@ class VortexParticleSystem : public ParticleSystem<VortexParticleData> {
}
virtual ParticleBase *clone();
public:
PbArgs _args;
}

View File

@ -240,6 +240,7 @@ class VortexSheetMesh : public Mesh {
VorticityChannel mVorticity;
TexCoord3Channel mTex1, mTex2;
TurbulenceChannel mTurb;
public:
PbArgs _args;
}

View File

@ -69,7 +69,8 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine,
experimental(false),
dicing_rate(1.0f),
max_subdivisions(12),
progress(progress)
progress(progress),
has_updates_(true)
{
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
dicing_rate = preview ? RNA_float_get(&cscene, "preview_dicing_rate") :
@ -84,7 +85,9 @@ BlenderSync::~BlenderSync()
void BlenderSync::reset(BL::BlendData &b_data, BL::Scene &b_scene)
{
/* Update data and scene pointers in case they change in session reset,
* for example after undo. */
* for example after undo.
* Note that we do not modify the `has_updates_` flag here because the sync
* reset is also used during viewport navigation. */
this->b_data = b_data;
this->b_scene = b_scene;
}
@ -117,6 +120,8 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
}
if (dicing_prop_changed) {
has_updates_ = true;
for (const pair<const GeometryKey, Geometry *> &iter : geometry_map.key_to_scene_data()) {
Geometry *geom = iter.second;
if (geom->is_mesh()) {
@ -133,6 +138,12 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
/* Iterate over all IDs in this depsgraph. */
for (BL::DepsgraphUpdate &b_update : b_depsgraph.updates) {
/* TODO(sergey): Can do more selective filter here. For example, ignore changes made to
* screen datablock. Note that sync_data() needs to be called after object deletion, and
* currently this is ensured by the scene ID tagged for update, which sets the `has_updates_`
* flag. */
has_updates_ = true;
BL::ID b_id(b_update.id());
/* Material */
@ -213,8 +224,18 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
if (b_v3d) {
BlenderViewportParameters new_viewport_parameters(b_v3d);
if (viewport_parameters.modified(new_viewport_parameters)) {
world_recalc = true;
has_updates_ = true;
}
if (!has_updates_) {
Film *film = scene->film;
const PassType new_display_pass = new_viewport_parameters.get_viewport_display_render_pass(
b_v3d);
has_updates_ |= film->get_display_pass() != new_display_pass;
}
}
}
@ -227,11 +248,15 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
int height,
void **python_thread_state)
{
if (!has_updates_) {
return;
}
scoped_timer timer;
BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
sync_view_layer(b_v3d, b_view_layer);
sync_view_layer(b_view_layer);
sync_integrator();
sync_film(b_v3d);
sync_shaders(b_depsgraph, b_v3d);
@ -254,6 +279,8 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
free_data_after_sync(b_depsgraph);
VLOG(1) << "Total time spent synchronizing data: " << timer.get_time();
has_updates_ = false;
}
/* Integrator */
@ -424,7 +451,7 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d)
/* Render Layer */
void BlenderSync::sync_view_layer(BL::SpaceView3D & /*b_v3d*/, BL::ViewLayer &b_view_layer)
void BlenderSync::sync_view_layer(BL::ViewLayer &b_view_layer)
{
view_layer.name = b_view_layer.name();

View File

@ -73,7 +73,7 @@ class BlenderSync {
int width,
int height,
void **python_thread_state);
void sync_view_layer(BL::SpaceView3D &b_v3d, BL::ViewLayer &b_view_layer);
void sync_view_layer(BL::ViewLayer &b_view_layer);
vector<Pass> sync_render_passes(BL::Scene &b_scene,
BL::RenderLayer &b_render_layer,
BL::ViewLayer &b_view_layer,
@ -264,6 +264,12 @@ class BlenderSync {
} view_layer;
Progress &progress;
protected:
/* Indicates that `sync_recalc()` detected changes in the scene.
* If this flag is false then the data is considered to be up-to-date and will not be
* synchronized at all. */
bool has_updates_ = true;
};
CCL_NAMESPACE_END

View File

@ -32,17 +32,26 @@ BlenderViewportParameters::BlenderViewportParameters()
BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d)
: BlenderViewportParameters()
{
if (!b_v3d) {
return;
}
BL::View3DShading shading = b_v3d.shading();
/* We only copy the parameters if we are in look dev mode. otherwise
* defaults are being used. These defaults mimic normal render settings */
if (b_v3d && b_v3d.shading().type() == BL::View3DShading::type_RENDERED) {
use_scene_world = b_v3d.shading().use_scene_world_render();
use_scene_lights = b_v3d.shading().use_scene_lights_render();
if (!use_scene_world) {
studiolight_rotate_z = b_v3d.shading().studiolight_rotate_z();
studiolight_intensity = b_v3d.shading().studiolight_intensity();
studiolight_background_alpha = b_v3d.shading().studiolight_background_alpha();
studiolight_path = b_v3d.shading().selected_studio_light().path();
}
if (shading.type() != BL::View3DShading::type_RENDERED) {
return;
}
use_scene_world = shading.use_scene_world_render();
use_scene_lights = shading.use_scene_lights_render();
if (!use_scene_world) {
studiolight_rotate_z = shading.studiolight_rotate_z();
studiolight_intensity = shading.studiolight_intensity();
studiolight_background_alpha = shading.studiolight_background_alpha();
studiolight_path = shading.selected_studio_light().path();
}
}

View File

@ -17,6 +17,8 @@
#ifdef WITH_OPTIX
# include "device/device.h"
# include "bvh/bvh_optix.h"
CCL_NAMESPACE_BEGIN
@ -26,6 +28,7 @@ BVHOptiX::BVHOptiX(const BVHParams &params_,
const vector<Object *> &objects_,
Device *device)
: BVH(params_, geometry_, objects_),
device(device),
traversable_handle(0),
as_data(device, params_.top_level ? "optix tlas" : "optix blas", false),
motion_transform_data(device, "optix motion transform", false)
@ -34,7 +37,9 @@ BVHOptiX::BVHOptiX(const BVHParams &params_,
BVHOptiX::~BVHOptiX()
{
// Acceleration structure memory is freed via the 'as_data' destructor
// Acceleration structure memory is delayed freed on device, since deleting the
// BVH may happen while still being used for rendering.
device->release_optix_bvh(this);
}
CCL_NAMESPACE_END

View File

@ -28,6 +28,7 @@ CCL_NAMESPACE_BEGIN
class BVHOptiX : public BVH {
public:
Device *device;
uint64_t traversable_handle;
device_only_memory<char> as_data;
device_only_memory<char> motion_transform_data;

View File

@ -61,7 +61,6 @@ enum DeviceTypeMask {
};
enum DeviceKernelStatus {
DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL = 0,
DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE,
DEVICE_KERNEL_USING_FEATURE_KERNEL,
DEVICE_KERNEL_FEATURE_KERNEL_INVALID,
@ -427,6 +426,9 @@ class Device {
/* acceleration structure building */
virtual void build_bvh(BVH *bvh, Progress &progress, bool refit);
/* OptiX specific destructor. */
virtual void release_optix_bvh(BVH * /*bvh*/){};
#ifdef WITH_NETWORK
/* networking */
void server_run();

View File

@ -35,10 +35,54 @@ device_memory::device_memory(Device *device, const char *name, MemoryType type)
device_pointer(0),
host_pointer(0),
shared_pointer(0),
shared_counter(0)
shared_counter(0),
original_device_ptr(0),
original_device_size(0),
original_device(0),
need_realloc_(false),
modified(false)
{
}
device_memory::device_memory(device_memory &&other) noexcept
: data_type(other.data_type),
data_elements(other.data_elements),
data_size(other.data_size),
device_size(other.device_size),
data_width(other.data_width),
data_height(other.data_height),
data_depth(other.data_depth),
type(other.type),
name(other.name),
device(other.device),
device_pointer(other.device_pointer),
host_pointer(other.host_pointer),
shared_pointer(other.shared_pointer),
shared_counter(other.shared_counter),
original_device_ptr(other.original_device_ptr),
original_device_size(other.original_device_size),
original_device(other.original_device),
need_realloc_(other.need_realloc_),
modified(other.modified)
{
other.data_elements = 0;
other.data_size = 0;
other.device_size = 0;
other.data_width = 0;
other.data_height = 0;
other.data_depth = 0;
other.device = 0;
other.device_pointer = 0;
other.host_pointer = 0;
other.shared_pointer = 0;
other.shared_counter = 0;
other.original_device_ptr = 0;
other.original_device_size = 0;
other.original_device = 0;
other.need_realloc_ = false;
other.modified = false;
}
device_memory::~device_memory()
{
assert(shared_pointer == 0);

View File

@ -238,6 +238,7 @@ class device_memory {
/* Only create through subclasses. */
device_memory(Device *device, const char *name, MemoryType type);
device_memory(device_memory &&other) noexcept;
/* No copying allowed. */
device_memory(const device_memory &) = delete;
@ -277,6 +278,10 @@ template<typename T> class device_only_memory : public device_memory {
data_elements = max(device_type_traits<T>::num_elements, 1);
}
device_only_memory(device_only_memory &&other) noexcept : device_memory(std::move(other))
{
}
virtual ~device_only_memory()
{
free();

View File

@ -232,10 +232,6 @@ class MultiDevice : public Device {
foreach (SubDevice &sub, devices) {
DeviceKernelStatus subresult = sub.device->get_active_kernel_switch_state();
switch (subresult) {
case DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL:
result = subresult;
break;
case DEVICE_KERNEL_FEATURE_KERNEL_INVALID:
case DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE:
return subresult;

View File

@ -193,6 +193,9 @@ class OptiXDevice : public CUDADevice {
device_only_memory<unsigned char> denoiser_state;
int denoiser_input_passes = 0;
vector<device_only_memory<char>> delayed_free_bvh_memory;
thread_mutex delayed_free_bvh_mutex;
public:
OptiXDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_)
: CUDADevice(info_, stats_, profiler_, background_),
@ -258,6 +261,8 @@ class OptiXDevice : public CUDADevice {
// Make CUDA context current
const CUDAContextScope scope(cuContext);
free_bvh_memory_delayed();
sbt_data.free();
texture_info.free();
launch_params.free();
@ -721,7 +726,11 @@ class OptiXDevice : public CUDADevice {
}
}
else if (task.type == DeviceTask::SHADER) {
launch_shader_eval(task, thread_index);
// CUDA kernels are used when doing baking
if (optix_module == NULL)
CUDADevice::shader(task);
else
launch_shader_eval(task, thread_index);
}
else if (task.type == DeviceTask::DENOISE_BUFFER) {
// Set up a single tile that covers the whole task and denoise it
@ -1297,6 +1306,8 @@ class OptiXDevice : public CUDADevice {
return;
}
free_bvh_memory_delayed();
BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh);
progress.set_substatus("Building OptiX acceleration structure");
@ -1767,6 +1778,24 @@ class OptiXDevice : public CUDADevice {
}
}
void release_optix_bvh(BVH *bvh) override
{
thread_scoped_lock lock(delayed_free_bvh_mutex);
/* Do delayed free of BVH memory, since geometry holding BVH might be deleted
* while GPU is still rendering. */
BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh);
delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->as_data));
delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->motion_transform_data));
bvh_optix->traversable_handle = 0;
}
void free_bvh_memory_delayed()
{
thread_scoped_lock lock(delayed_free_bvh_mutex);
delayed_free_bvh_memory.free_memory();
}
void const_copy_to(const char *name, void *host, size_t size) override
{
// Set constant memory for CUDA module

View File

@ -269,7 +269,6 @@ class OpenCLDevice : public Device {
cl_device_id cdDevice;
cl_int ciErr;
int device_num;
bool use_preview_kernels;
class OpenCLProgram {
public:
@ -369,8 +368,7 @@ class OpenCLDevice : public Device {
/* Load the kernels and put the created kernels in the given
* `programs` parameter. */
void load_kernels(vector<OpenCLProgram *> &programs,
const DeviceRequestedFeatures &requested_features,
bool is_preview = false);
const DeviceRequestedFeatures &requested_features);
};
DeviceSplitKernel *split_kernel;
@ -382,7 +380,6 @@ class OpenCLDevice : public Device {
OpenCLProgram denoising_program;
OpenCLSplitPrograms kernel_programs;
OpenCLSplitPrograms preview_programs;
typedef map<string, device_vector<uchar> *> ConstMemMap;
typedef map<string, device_ptr> MemMap;
@ -412,7 +409,6 @@ class OpenCLDevice : public Device {
string device_md5_hash(string kernel_custom_build_options = "");
bool load_kernels(const DeviceRequestedFeatures &requested_features);
void load_required_kernels(const DeviceRequestedFeatures &requested_features);
void load_preview_kernels();
bool wait_for_availability(const DeviceRequestedFeatures &requested_features);
DeviceKernelStatus get_active_kernel_switch_state();
@ -422,8 +418,7 @@ class OpenCLDevice : public Device {
/* Get the program file name to compile (*.cl) for the given kernel */
const string get_opencl_program_filename(const string &kernel_name);
string get_build_options(const DeviceRequestedFeatures &requested_features,
const string &opencl_program_name,
bool preview_kernel = false);
const string &opencl_program_name);
/* Enable the default features to reduce recompilation events */
void enable_default_features(DeviceRequestedFeatures &features);

View File

@ -107,8 +107,7 @@ void OpenCLDevice::enable_default_features(DeviceRequestedFeatures &features)
}
string OpenCLDevice::get_build_options(const DeviceRequestedFeatures &requested_features,
const string &opencl_program_name,
bool preview_kernel)
const string &opencl_program_name)
{
/* first check for non-split kernel programs */
if (opencl_program_name == "base" || opencl_program_name == "denoising") {
@ -185,13 +184,7 @@ string OpenCLDevice::get_build_options(const DeviceRequestedFeatures &requested_
enable_default_features(nofeatures);
/* Add program specific optimized compile directives */
if (preview_kernel) {
DeviceRequestedFeatures preview_features;
preview_features.use_hair = true;
build_options += "-D__KERNEL_AO_PREVIEW__ ";
build_options += preview_features.get_build_options();
}
else if (opencl_program_name == "split_do_volume" && !requested_features.use_volume) {
if (opencl_program_name == "split_do_volume" && !requested_features.use_volume) {
build_options += nofeatures.get_build_options();
}
else {
@ -238,9 +231,7 @@ OpenCLDevice::OpenCLSplitPrograms::~OpenCLSplitPrograms()
}
void OpenCLDevice::OpenCLSplitPrograms::load_kernels(
vector<OpenCLProgram *> &programs,
const DeviceRequestedFeatures &requested_features,
bool is_preview)
vector<OpenCLProgram *> &programs, const DeviceRequestedFeatures &requested_features)
{
if (!requested_features.use_baking) {
# define ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(kernel_name) \
@ -251,7 +242,7 @@ void OpenCLDevice::OpenCLSplitPrograms::load_kernels(
device, \
program_name_##kernel_name, \
"kernel_" #kernel_name ".cl", \
device->get_build_options(requested_features, program_name_##kernel_name, is_preview)); \
device->get_build_options(requested_features, program_name_##kernel_name)); \
program_##kernel_name.add_kernel(ustring("path_trace_" #kernel_name)); \
programs.push_back(&program_##kernel_name);
@ -259,7 +250,7 @@ void OpenCLDevice::OpenCLSplitPrograms::load_kernels(
ADD_SPLIT_KERNEL_PROGRAM(subsurface_scatter);
ADD_SPLIT_KERNEL_PROGRAM(direct_lighting);
ADD_SPLIT_KERNEL_PROGRAM(indirect_background);
if (requested_features.use_volume || is_preview) {
if (requested_features.use_volume) {
ADD_SPLIT_KERNEL_PROGRAM(do_volume);
}
ADD_SPLIT_KERNEL_PROGRAM(shader_eval);
@ -274,7 +265,7 @@ void OpenCLDevice::OpenCLSplitPrograms::load_kernels(
device,
"split_bundle",
"kernel_split_bundle.cl",
device->get_build_options(requested_features, "split_bundle", is_preview));
device->get_build_options(requested_features, "split_bundle"));
ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(data_init);
ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(state_buffer_size);
@ -403,7 +394,7 @@ class OpenCLSplitKernel : public DeviceSplitKernel {
device,
program_name,
device->get_opencl_program_filename(kernel_name),
device->get_build_options(requested_features, program_name, device->use_preview_kernels));
device->get_build_options(requested_features, program_name));
kernel->program.add_kernel(ustring("path_trace_" + kernel_name));
kernel->program.load();
@ -617,7 +608,6 @@ OpenCLDevice::OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, b
: Device(info, stats, profiler, background),
load_kernel_num_compiling(0),
kernel_programs(this),
preview_programs(this),
memory_manager(this),
texture_info(this, "__texture_info", MEM_GLOBAL)
{
@ -627,7 +617,6 @@ OpenCLDevice::OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, b
cqCommandQueue = NULL;
device_initialized = false;
textures_need_update = true;
use_preview_kernels = !background;
vector<OpenCLPlatformDevice> usable_devices;
OpenCLInfo::get_usable_devices(&usable_devices);
@ -683,9 +672,6 @@ OpenCLDevice::OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, b
device_initialized = true;
split_kernel = new OpenCLSplitKernel(this);
if (use_preview_kernels) {
load_preview_kernels();
}
}
OpenCLDevice::~OpenCLDevice()
@ -776,7 +762,7 @@ bool OpenCLDevice::load_kernels(const DeviceRequestedFeatures &requested_feature
load_required_kernels(requested_features);
vector<OpenCLProgram *> programs;
kernel_programs.load_kernels(programs, requested_features, false);
kernel_programs.load_kernels(programs, requested_features);
if (!requested_features.use_baking && requested_features.use_denoising) {
denoising_program = OpenCLProgram(
@ -854,19 +840,6 @@ void OpenCLDevice::load_required_kernels(const DeviceRequestedFeatures &requeste
}
}
void OpenCLDevice::load_preview_kernels()
{
DeviceRequestedFeatures no_features;
vector<OpenCLProgram *> programs;
preview_programs.load_kernels(programs, no_features, true);
foreach (OpenCLProgram *program, programs) {
if (!program->load()) {
load_required_kernel_task_pool.push(function_bind(&OpenCLProgram::compile, program));
}
}
}
bool OpenCLDevice::wait_for_availability(const DeviceRequestedFeatures &requested_features)
{
if (requested_features.use_baking) {
@ -874,59 +847,18 @@ bool OpenCLDevice::wait_for_availability(const DeviceRequestedFeatures &requeste
return true;
}
if (background) {
load_kernel_task_pool.wait_work();
use_preview_kernels = false;
}
else {
/* We use a device setting to determine to load preview kernels or not
* Better to check on device level than per kernel as mixing preview and
* non-preview kernels does not work due to different data types */
if (use_preview_kernels) {
use_preview_kernels = load_kernel_num_compiling.load() > 0;
}
}
load_kernel_task_pool.wait_work();
return split_kernel->load_kernels(requested_features);
}
OpenCLDevice::OpenCLSplitPrograms *OpenCLDevice::get_split_programs()
{
return use_preview_kernels ? &preview_programs : &kernel_programs;
return &kernel_programs;
}
DeviceKernelStatus OpenCLDevice::get_active_kernel_switch_state()
{
/* Do not switch kernels for background renderings
* We do foreground rendering but use the preview kernels
* Check for the optimized kernels
*
* This works also the other way around, where we are using
* optimized kernels but new ones are being compiled due
* to other features that are needed */
if (background) {
/* The if-statements below would find the same result,
* But as the `finished` method uses a mutex we added
* this as an early exit */
return DEVICE_KERNEL_USING_FEATURE_KERNEL;
}
bool other_kernels_finished = load_kernel_num_compiling.load() == 0;
if (use_preview_kernels) {
if (other_kernels_finished) {
return DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE;
}
else {
return DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL;
}
}
else {
if (other_kernels_finished) {
return DEVICE_KERNEL_USING_FEATURE_KERNEL;
}
else {
return DEVICE_KERNEL_FEATURE_KERNEL_INVALID;
}
}
return DEVICE_KERNEL_USING_FEATURE_KERNEL;
}
void OpenCLDevice::mem_alloc(device_memory &mem)

View File

@ -99,27 +99,23 @@ CCL_NAMESPACE_BEGIN
#define __AO__
#define __PASSES__
#define __HAIR__
/* Without these we get an AO render, used by OpenCL preview kernel. */
#ifndef __KERNEL_AO_PREVIEW__
# define __SVM__
# define __EMISSION__
# define __HOLDOUT__
# define __MULTI_CLOSURE__
# define __TRANSPARENT_SHADOWS__
# define __BACKGROUND_MIS__
# define __LAMP_MIS__
# define __CAMERA_MOTION__
# define __OBJECT_MOTION__
# define __BAKING__
# define __PRINCIPLED__
# define __SUBSURFACE__
# define __VOLUME__
# define __VOLUME_SCATTER__
# define __CMJ__
# define __SHADOW_RECORD_ALL__
# define __BRANCHED_PATH__
#endif
#define __SVM__
#define __EMISSION__
#define __HOLDOUT__
#define __MULTI_CLOSURE__
#define __TRANSPARENT_SHADOWS__
#define __BACKGROUND_MIS__
#define __LAMP_MIS__
#define __CAMERA_MOTION__
#define __OBJECT_MOTION__
#define __BAKING__
#define __PRINCIPLED__
#define __SUBSURFACE__
#define __VOLUME__
#define __VOLUME_SCATTER__
#define __CMJ__
#define __SHADOW_RECORD_ALL__
#define __BRANCHED_PATH__
/* Device specific features */
#ifdef __KERNEL_CPU__

View File

@ -25,7 +25,7 @@
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
* The offset's components are in the range [100, 200], not too high to cause
* bad precision and not to small to be noticeable. We use float seed because
* bad precision and not too small to be noticeable. We use float seed because
* OSL only support float hashes.
*/

View File

@ -20,7 +20,7 @@ CCL_NAMESPACE_BEGIN
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
* The offset's components are in the range [100, 200], not too high to cause
* bad precision and not to small to be noticeable. We use float seed because
* bad precision and not too small to be noticeable. We use float seed because
* OSL only support float hashes.
*/

View File

@ -498,12 +498,8 @@ void AlembicObject::load_data_in_cache(CachedData &cached_data,
/* Use the schema as the base compound property to also be able to look for top level properties.
*/
read_attributes(proc,
cached_data,
schema.getArbGeomParams(),
schema.getUVsParam(),
get_requested_attributes(),
progress);
read_attributes(
proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
cached_data.invalidate_last_loaded_time(true);
data_loaded = true;
@ -658,8 +654,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
list<Attribute>::iterator it;
for (it = attributes.attributes.begin(); it != attributes.attributes.end();) {
if (cached_attributes.find(&(*it)) == cached_attributes.end()) {
attributes.attributes.erase(it++);
attributes.modified = true;
attributes.remove(it++);
continue;
}

View File

@ -894,6 +894,10 @@ static void parse_requested_attributes_recursive(const AttributeRequestSet &requ
const ICompoundProperty &arb_geom_params,
vector<PropHeaderAndParent> &requested_properties)
{
if (!arb_geom_params.valid()) {
return;
}
for (const AttributeRequest &req : requested_attributes.requests) {
const PropertyHeader *property_header = arb_geom_params.getPropertyHeader(req.name.c_str());

View File

@ -383,6 +383,23 @@ AttributeStandard Attribute::name_standard(const char *name)
return ATTR_STD_NONE;
}
AttrKernelDataType Attribute::kernel_type(const Attribute &attr)
{
if (attr.element == ATTR_ELEMENT_CORNER) {
return AttrKernelDataType::UCHAR4;
}
if (attr.type == TypeDesc::TypeFloat) {
return AttrKernelDataType::FLOAT;
}
if (attr.type == TypeFloat2) {
return AttrKernelDataType::FLOAT2;
}
return AttrKernelDataType::FLOAT3;
}
void Attribute::get_uv_tiles(Geometry *geom,
AttributePrimitive prim,
unordered_set<int> &tiles) const
@ -417,7 +434,7 @@ void Attribute::get_uv_tiles(Geometry *geom,
/* Attribute Set */
AttributeSet::AttributeSet(Geometry *geometry, AttributePrimitive prim)
: geometry(geometry), prim(prim)
: modified_flag(~0u), geometry(geometry), prim(prim)
{
}
@ -440,7 +457,7 @@ Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement eleme
Attribute new_attr(name, type, element, geometry, prim);
attributes.emplace_back(std::move(new_attr));
modified = true;
tag_modified(attributes.back());
return &attributes.back();
}
@ -462,8 +479,7 @@ void AttributeSet::remove(ustring name)
for (it = attributes.begin(); it != attributes.end(); it++) {
if (&*it == attr) {
modified = true;
attributes.erase(it);
remove(it);
return;
}
}
@ -608,8 +624,7 @@ void AttributeSet::remove(AttributeStandard std)
for (it = attributes.begin(); it != attributes.end(); it++) {
if (&*it == attr) {
modified = true;
attributes.erase(it);
remove(it);
return;
}
}
@ -634,6 +649,12 @@ void AttributeSet::remove(Attribute *attribute)
}
}
void AttributeSet::remove(list<Attribute>::iterator it)
{
tag_modified(*it);
attributes.erase(it);
}
void AttributeSet::resize(bool reserve_only)
{
foreach (Attribute &attr, attributes) {
@ -674,15 +695,13 @@ void AttributeSet::update(AttributeSet &&new_attributes)
for (it = attributes.begin(); it != attributes.end();) {
if (it->std != ATTR_STD_NONE) {
if (new_attributes.find(it->std) == nullptr) {
modified = true;
attributes.erase(it++);
remove(it++);
continue;
}
}
else if (it->name != "") {
if (new_attributes.find(it->name) == nullptr) {
modified = true;
attributes.erase(it++);
remove(it++);
continue;
}
}
@ -699,7 +718,27 @@ void AttributeSet::clear_modified()
foreach (Attribute &attr, attributes) {
attr.modified = false;
}
modified = false;
modified_flag = 0;
}
void AttributeSet::tag_modified(const Attribute &attr)
{
/* Some attributes are not stored in the various kernel attribute arrays
* (DeviceScene::attribute_*), so the modified flags are only set if the associated standard
* corresponds to an attribute which will be stored in the kernel's attribute arrays. */
const bool modifies_device_array = (attr.std != ATTR_STD_FACE_NORMAL &&
attr.std != ATTR_STD_VERTEX_NORMAL);
if (modifies_device_array) {
AttrKernelDataType kernel_type = Attribute::kernel_type(attr);
modified_flag |= (1u << kernel_type);
}
}
bool AttributeSet::modified(AttrKernelDataType kernel_type) const
{
return (modified_flag & (1u << kernel_type)) != 0;
}
/* AttributeRequest */

View File

@ -39,6 +39,21 @@ class Hair;
class Mesh;
struct Transform;
/* AttrKernelDataType.
*
* The data type of the device arrays storing the attribute's data. Those data types are different
* than the ones for attributes as some attribute types are stored in the same array, e.g. Point,
* Vector, and Transform are all stored as float3 in the kernel.
*
* The values of this enumeration are also used as flags to detect changes in AttributeSet. */
enum AttrKernelDataType {
FLOAT = 0,
FLOAT2 = 1,
FLOAT3 = 2,
UCHAR4 = 3,
};
/* Attribute
*
* Arbitrary data layers on meshes.
@ -167,6 +182,8 @@ class Attribute {
static const char *standard_name(AttributeStandard std);
static AttributeStandard name_standard(const char *name);
static AttrKernelDataType kernel_type(const Attribute &attr);
void get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set<int> &tiles) const;
};
@ -175,11 +192,12 @@ class Attribute {
* Set of attributes on a mesh. */
class AttributeSet {
uint32_t modified_flag;
public:
Geometry *geometry;
AttributePrimitive prim;
list<Attribute> attributes;
bool modified = true;
AttributeSet(Geometry *geometry, AttributePrimitive prim);
AttributeSet(AttributeSet &&) = default;
@ -197,6 +215,8 @@ class AttributeSet {
void remove(Attribute *attribute);
void remove(list<Attribute>::iterator it);
void resize(bool reserve_only = false);
void clear(bool preserve_voxel_data = false);
@ -204,7 +224,18 @@ class AttributeSet {
* and remove any attribute not found on the new set from this. */
void update(AttributeSet &&new_attributes);
/* Return whether the attributes of the given kernel_type are modified, where "modified" means
* that some attributes of the given type were added or removed from this AttributeSet. This does
* not mean that the data of the remaining attributes in this AttributeSet were also modified. To
* check this, use Attribute.modified. */
bool modified(AttrKernelDataType kernel_type) const;
void clear_modified();
private:
/* Set the relevant modified flag for the attribute. Only attributes that are stored in device
* arrays will be considered for tagging this AttributeSet as modified. */
void tag_modified(const Attribute &attr);
};
/* AttributeRequest

View File

@ -830,10 +830,13 @@ void GeometryManager::device_update_attributes(Device *device,
dscene->attributes_float3.alloc(attr_float3_size);
dscene->attributes_uchar4.alloc(attr_uchar4_size);
const bool copy_all_data = dscene->attributes_float.need_realloc() ||
dscene->attributes_float2.need_realloc() ||
dscene->attributes_float3.need_realloc() ||
dscene->attributes_uchar4.need_realloc();
/* The order of those flags needs to match that of AttrKernelDataType. */
const bool attributes_need_realloc[4] = {
dscene->attributes_float.need_realloc(),
dscene->attributes_float2.need_realloc(),
dscene->attributes_float3.need_realloc(),
dscene->attributes_uchar4.need_realloc(),
};
size_t attr_float_offset = 0;
size_t attr_float2_offset = 0;
@ -852,7 +855,7 @@ void GeometryManager::device_update_attributes(Device *device,
if (attr) {
/* force a copy if we need to reallocate all the data */
attr->modified |= copy_all_data;
attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)];
}
update_attribute_element_offset(geom,
@ -875,7 +878,7 @@ void GeometryManager::device_update_attributes(Device *device,
if (subd_attr) {
/* force a copy if we need to reallocate all the data */
subd_attr->modified |= copy_all_data;
subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)];
}
update_attribute_element_offset(mesh,
@ -906,6 +909,10 @@ void GeometryManager::device_update_attributes(Device *device,
foreach (AttributeRequest &req, attributes.requests) {
Attribute *attr = values.find(req);
if (attr) {
attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)];
}
update_attribute_element_offset(object->geometry,
dscene->attributes_float,
attr_float_offset,
@ -941,10 +948,10 @@ void GeometryManager::device_update_attributes(Device *device,
/* copy to device */
progress.set_status("Updating Mesh", "Copying Attributes to device");
dscene->attributes_float.copy_to_device();
dscene->attributes_float2.copy_to_device();
dscene->attributes_float3.copy_to_device();
dscene->attributes_uchar4.copy_to_device();
dscene->attributes_float.copy_to_device_if_modified();
dscene->attributes_float2.copy_to_device_if_modified();
dscene->attributes_float3.copy_to_device_if_modified();
dscene->attributes_uchar4.copy_to_device_if_modified();
if (progress.get_cancel())
return;
@ -1431,24 +1438,46 @@ static void update_device_flags_attribute(uint32_t &device_update_flags,
continue;
}
if (attr.element == ATTR_ELEMENT_CORNER) {
device_update_flags |= ATTR_UCHAR4_MODIFIED;
}
else if (attr.type == TypeDesc::TypeFloat) {
device_update_flags |= ATTR_FLOAT_MODIFIED;
}
else if (attr.type == TypeFloat2) {
device_update_flags |= ATTR_FLOAT2_MODIFIED;
}
else if (attr.type == TypeDesc::TypeMatrix) {
device_update_flags |= ATTR_FLOAT3_MODIFIED;
}
else if (attr.element != ATTR_ELEMENT_VOXEL) {
device_update_flags |= ATTR_FLOAT3_MODIFIED;
AttrKernelDataType kernel_type = Attribute::kernel_type(attr);
switch (kernel_type) {
case AttrKernelDataType::FLOAT: {
device_update_flags |= ATTR_FLOAT_MODIFIED;
break;
}
case AttrKernelDataType::FLOAT2: {
device_update_flags |= ATTR_FLOAT2_MODIFIED;
break;
}
case AttrKernelDataType::FLOAT3: {
device_update_flags |= ATTR_FLOAT3_MODIFIED;
break;
}
case AttrKernelDataType::UCHAR4: {
device_update_flags |= ATTR_UCHAR4_MODIFIED;
break;
}
}
}
}
static void update_attribute_realloc_flags(uint32_t &device_update_flags,
const AttributeSet &attributes)
{
if (attributes.modified(AttrKernelDataType::FLOAT)) {
device_update_flags |= ATTR_FLOAT_NEEDS_REALLOC;
}
if (attributes.modified(AttrKernelDataType::FLOAT2)) {
device_update_flags |= ATTR_FLOAT2_NEEDS_REALLOC;
}
if (attributes.modified(AttrKernelDataType::FLOAT3)) {
device_update_flags |= ATTR_FLOAT3_NEEDS_REALLOC;
}
if (attributes.modified(AttrKernelDataType::UCHAR4)) {
device_update_flags |= ATTR_UCHAR4_NEEDS_REALLOC;
}
}
void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress)
{
if (!need_update() && !need_flags_update) {
@ -1471,16 +1500,11 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
foreach (Geometry *geom, scene->geometry) {
geom->has_volume = false;
if (geom->attributes.modified) {
device_update_flags |= ATTRS_NEED_REALLOC;
}
update_attribute_realloc_flags(device_update_flags, geom->attributes);
if (geom->is_mesh()) {
Mesh *mesh = static_cast<Mesh *>(geom);
if (mesh->subd_attributes.modified) {
device_update_flags |= ATTRS_NEED_REALLOC;
}
update_attribute_realloc_flags(device_update_flags, mesh->subd_attributes);
}
foreach (Node *node, geom->get_used_shaders()) {

View File

@ -542,9 +542,6 @@ bool Scene::update(Progress &progress, bool &kernel_switch_needed)
DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state();
kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE ||
kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID;
if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
progress.set_kernel_status("Compiling render kernels");
}
if (new_kernels_needed || kernel_switch_needed) {
progress.set_kernel_status("Compiling render kernels");
device->wait_for_availability(loaded_kernel_features);

View File

@ -243,11 +243,6 @@ void Session::run_gpu()
}
}
/* Don't go in pause mode when image was rendered with preview kernels
* When feature kernels become available the session will be reset. */
else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
time_sleep(0.1);
}
else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) {
reset_gpu(tile_manager.params, params.samples);
}
@ -762,11 +757,6 @@ void Session::run_cpu()
}
}
/* Don't go in pause mode when preview kernels are used
* When feature kernels become available the session will be reset. */
else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
time_sleep(0.1);
}
else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) {
reset_cpu(tile_manager.params, params.samples);
}

View File

@ -43,8 +43,8 @@ class vector : public std::vector<value_type, allocator_type> {
/* Try as hard as possible to use zero memory. */
void free_memory()
{
BaseClass::resize(0);
BaseClass::shrink_to_fit();
vector<value_type, allocator_type> empty;
BaseClass::swap(empty);
}
/* Some external API might demand working with std::vector. */

View File

@ -121,22 +121,30 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
monitor.dwFlags = 0;
GetMonitorInfo(MonitorFromRect(&win_rect, MONITOR_DEFAULTTONEAREST), &monitor);
/* Adjust our requested size to allow for caption and borders and constrain to monitor. */
AdjustWindowRectEx(&win_rect, WS_CAPTION, FALSE, 0);
/* Constrain requested size and position to fit within this monitor. */
width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect.right - win_rect.left);
left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width);
height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect.bottom - win_rect.top);
top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height);
win_rect.left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width);
win_rect.right = win_rect.left + width;
win_rect.top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height);
win_rect.bottom = win_rect.top + height;
m_hWnd = ::CreateWindowExW(extended_style, // window extended style
s_windowClassName, // pointer to registered class name
title_16, // pointer to window name
style, // window style
left, // horizontal position of window
top, // vertical position of window
width, // window width
height, // window height
m_parentWindowHwnd, // handle to parent or owner window
/* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be
* correctly outside of monitor bounds. Note: You cannot specify WS_OVERLAPPED when calling. */
AdjustWindowRectEx(&win_rect, style & ~WS_OVERLAPPED, FALSE, extended_style);
/* But never allow a top position that can hide part of the title bar. */
win_rect.top = max(monitor.rcWork.top, win_rect.top);
m_hWnd = ::CreateWindowExW(extended_style, // window extended style
s_windowClassName, // pointer to registered class name
title_16, // pointer to window name
style, // window style
win_rect.left, // horizontal position of window
win_rect.top, // vertical position of window
win_rect.right - win_rect.left, // window width
win_rect.bottom - win_rect.top, // window height
m_parentWindowHwnd, // handle to parent or owner window
0, // handle to menu or child-window identifier
::GetModuleHandle(0), // handle to application instance
0); // pointer to window-creation data

View File

@ -242,7 +242,7 @@ typedef enum {
} GHOST_MouseCaptureEventWin32;
/**
* GHOST window on M$ Windows OSs.
* GHOST window on MS Windows OSs.
*/
class GHOST_WindowWin32 : public GHOST_Window {
public:

View File

@ -48,7 +48,7 @@
inline void copy_ghost_pose_to_openxr_pose(const GHOST_XrPose &ghost_pose, XrPosef &r_oxr_pose)
{
/* Set and convert to OpenXR coodinate space. */
/* Set and convert to OpenXR coordinate space. */
r_oxr_pose.position.x = ghost_pose.position[0];
r_oxr_pose.position.y = ghost_pose.position[1];
r_oxr_pose.position.z = ghost_pose.position[2];

View File

@ -364,7 +364,7 @@ public:
//! Along an arbitrary axes. It is not necessary to normalize rotaxis.
//! returns identity rotation matrix in the case that the norm of rotaxis
//! is to small to be used.
//! is too small to be used.
// @see Rot2 if you want to handle this error in another way.
static Rotation Rot(const Vector& rotaxis,double angle);

View File

@ -161,7 +161,7 @@ class Error_MotionPlanning : public Error {};
class Error_MotionPlanning_Circle_ToSmall : public Error_MotionPlanning {
public:
virtual const char* Description() const { return "Circle : radius is to small";}
virtual const char* Description() const { return "Circle : radius is too small";}
virtual int GetType() const {return 3001;}
};

View File

@ -0,0 +1,4 @@
# MikkTSpace
A common standard for tangent space used in baking tools to produce normal maps.
More information can be found at http://www.mikktspace.com/.

View File

@ -27,12 +27,11 @@ class Version:
def __str__(self) -> str:
return self.version
def get_download_file_names(version: Version):
yield f"blender-{version}-linux-x86_64.tar.xz"
yield f"blender-{version}-darwin-x86_64.dmg"
yield f"blender-{version}-windows-amd64.msi"
yield f"blender-{version}-windows-amd64.zip"
yield f"blender-{version}-linux-x64.tar.xz"
yield f"blender-{version}-macos-x64.dmg"
yield f"blender-{version}-windows-x64.msi"
yield f"blender-{version}-windows-x64.zip"
def get_download_url(version: Version, file_name: str) -> str:

View File

@ -16,7 +16,7 @@
#
# ##### END GPL LICENSE BLOCK #####
# Filename : ignore_small_oclusions.py
# Filename : ignore_small_occlusions.py
# Author : Stephane Grabli
# Date : 04/08/2005
# Purpose : The strokes are drawn through small occlusions

View File

@ -37,6 +37,9 @@ OBJECT_TYPES_RENDER = {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT'}
def ids_nolib(bids):
return (bid for bid in bids if not bid.library)
def ids_nolib_with_preview(bids):
return (bid for bid in bids if (not bid.library and bid.preview))
def rna_backup_gen(data, include_props=None, exclude_props=None, root=()):
# only writable properties...
@ -313,8 +316,9 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
image = bpy.data.images[render_context.image, None]
item = getattr(bpy.data, item_container)[item_name, None]
image.reload()
item.preview.image_size = (RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE)
item.preview.image_pixels_float[:] = image.pixels
preview = item.preview_ensure()
preview.image_size = (RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE)
preview.image_pixels_float[:] = image.pixels
# And now, main code!
do_save = True
@ -451,15 +455,15 @@ def do_clear_previews(do_objects, do_collections, do_scenes, do_data_intern):
bpy.ops.wm.previews_clear(id_type={'SHADING'})
if do_objects:
for ob in ids_nolib(bpy.data.objects):
for ob in ids_nolib_with_preview(bpy.data.objects):
ob.preview.image_size = (0, 0)
if do_collections:
for grp in ids_nolib(bpy.data.collections):
for grp in ids_nolib_with_preview(bpy.data.collections):
grp.preview.image_size = (0, 0)
if do_scenes:
for scene in ids_nolib(bpy.data.scenes):
for scene in ids_nolib_with_preview(bpy.data.scenes):
scene.preview.image_size = (0, 0)
print("Saving %s..." % bpy.data.filepath)

View File

@ -296,7 +296,7 @@ def bake_action_iter(
pbone.keyframe_insert("rotation_axis_angle", index=-1, frame=f, group=name)
else: # euler, XYZ, ZXY etc
if euler_prev is not None:
euler = pbone.matrix_basis.to_euler(obj.rotation_mode, euler_prev)
euler = pbone.matrix_basis.to_euler(pbone.rotation_mode, euler_prev)
pbone.rotation_euler = euler
del euler
euler_prev = pbone.rotation_euler.copy()

View File

@ -34,7 +34,7 @@ __all__ = (
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'
return space_data and space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'ASSETS'
@classmethod
def is_asset_browser_poll(cls, context: Context):

View File

@ -421,7 +421,7 @@ def draw_keymaps(context, layout):
rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM')
if not filter_text:
# When the keyconfig defines it's own preferences.
# When the keyconfig defines its own preferences.
kc_prefs = kc_active.preferences
if kc_prefs is not None:
box = col.box()

View File

@ -4485,8 +4485,7 @@ def km_sculpt(params):
items.extend([
# Transfer Sculpt Mode (release to avoid conflict with grease pencil drawing).
("object.transfer_mode", {"type": 'D', "value": 'RELEASE'},
{"properties": [("use_eyedropper", False), ("flash_object", True)]}),
("object.transfer_mode", {"type": 'D', "value": 'RELEASE'}, None),
# Brush strokes
("sculpt.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("mode", 'NORMAL')]}),
@ -5027,32 +5026,30 @@ def km_object_non_modal(params):
{"properties": [("mode", 'VERTEX_PAINT'), ("toggle", True)]}),
("object.mode_set", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'WEIGHT_PAINT'), ("toggle", True)]}),
])
elif params.use_pie_click_drag:
items.extend([
("object.mode_set", {"type": 'TAB', "value": 'CLICK'},
{"properties": [("mode", 'EDIT'), ("toggle", True)]}),
op_menu_pie("VIEW3D_MT_object_mode_pie", {"type": 'TAB', "value": 'CLICK_DRAG'}),
("view3d.object_mode_pie_or_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None),
])
elif not params.use_v3d_tab_menu:
items.extend([
("object.mode_set", {"type": 'TAB', "value": 'PRESS'},
{"properties": [("mode", 'EDIT'), ("toggle", True)]}),
("view3d.object_mode_pie_or_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None),
])
else:
# Swap Tab/Ctrl-Tab
items.extend([
("object.mode_set", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'EDIT'), ("toggle", True)]}),
op_menu_pie("VIEW3D_MT_object_mode_pie", {"type": 'TAB', "value": 'PRESS'}),
])
if params.legacy:
items.extend([
("object.origin_set", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
])
else:
if params.use_pie_click_drag:
items.extend([
("object.mode_set", {"type": 'TAB', "value": 'CLICK'},
{"properties": [("mode", 'EDIT'), ("toggle", True)]}),
op_menu_pie("VIEW3D_MT_object_mode_pie", {"type": 'TAB', "value": 'CLICK_DRAG'}),
("view3d.object_mode_pie_or_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None),
])
elif params.use_v3d_tab_menu:
# Swap Tab/Ctrl-Tab
items.extend([
("object.mode_set", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'EDIT'), ("toggle", True)]}),
op_menu_pie("VIEW3D_MT_object_mode_pie", {"type": 'TAB', "value": 'PRESS'}),
])
else:
items.extend([
("object.mode_set", {"type": 'TAB', "value": 'PRESS'},
{"properties": [("mode", 'EDIT'), ("toggle", True)]}),
("view3d.object_mode_pie_or_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None),
])
return keymap

View File

@ -206,8 +206,8 @@ class CLIP_OT_filter_tracks(Operator):
@classmethod
def poll(cls, context):
space = context.space_data
return (space.type == 'CLIP_EDITOR') and space.clip
sc = context.space_data
return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
num_tracks = self._filter_values(context, self.track_threshold)
@ -221,8 +221,8 @@ class CLIP_OT_set_active_clip(Operator):
@classmethod
def poll(cls, context):
space = context.space_data
return space.type == 'CLIP_EDITOR' and space.clip
sc = context.space_data
return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
clip = context.space_data.clip
@ -268,8 +268,8 @@ class CLIP_OT_track_to_empty(Operator):
@classmethod
def poll(cls, context):
space = context.space_data
return space.type == 'CLIP_EDITOR' and space.clip
sc = context.space_data
return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
sc = context.space_data
@ -293,7 +293,7 @@ class CLIP_OT_bundles_to_mesh(Operator):
@classmethod
def poll(cls, context):
sc = context.space_data
return (sc.type == 'CLIP_EDITOR') and sc.clip
return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
from bpy_extras.io_utils import unpack_list
@ -341,12 +341,8 @@ class CLIP_OT_delete_proxy(Operator):
@classmethod
def poll(cls, context):
if context.space_data.type != 'CLIP_EDITOR':
return False
sc = context.space_data
return sc.clip
return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def invoke(self, context, event):
wm = context.window_manager
@ -424,12 +420,8 @@ class CLIP_OT_set_viewport_background(Operator):
@classmethod
def poll(cls, context):
if context.space_data.type != 'CLIP_EDITOR':
return False
sc = context.space_data
return sc.clip
return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
sc = context.space_data
@ -563,13 +555,11 @@ class CLIP_OT_setup_tracking_scene(Operator):
@classmethod
def poll(cls, context):
sc = context.space_data
if sc.type != 'CLIP_EDITOR':
return False
clip = sc.clip
return clip and clip.tracking.reconstruction.is_valid
if sc and sc.type == 'CLIP_EDITOR':
clip = sc.clip
if clip and clip.tracking.reconstruction.is_valid:
return True
return False
@staticmethod
def _setupScene(context):
@ -1018,13 +1008,11 @@ class CLIP_OT_track_settings_as_default(Operator):
@classmethod
def poll(cls, context):
sc = context.space_data
if sc.type != 'CLIP_EDITOR':
return False
clip = sc.clip
return clip and clip.tracking.tracks.active
if sc and sc.type == 'CLIP_EDITOR':
clip = sc.clip
if clip and clip.tracking.tracks.active:
return True
return False
def execute(self, context):
sc = context.space_data
@ -1068,11 +1056,12 @@ class CLIP_OT_track_settings_to_track(Operator):
@classmethod
def poll(cls, context):
space = context.space_data
if space.type != 'CLIP_EDITOR':
return False
clip = space.clip
return clip and clip.tracking.tracks.active
sc = context.space_data
if sc and sc.type == 'CLIP_EDITOR':
clip = sc.clip
if clip and clip.tracking.tracks.active:
return True
return False
def execute(self, context):
space = context.space_data

View File

@ -33,6 +33,11 @@ class CONSTRAINT_OT_add_target(Operator):
bl_label = "Add Target"
bl_options = {'UNDO', 'INTERNAL'}
@classmethod
def poll(cls, context):
constraint = getattr(context, "constraint", None)
return constraint
def execute(self, context):
context.constraint.targets.new()
return {'FINISHED'}
@ -46,6 +51,11 @@ class CONSTRAINT_OT_remove_target(Operator):
index: IntProperty()
@classmethod
def poll(cls, context):
constraint = getattr(context, "constraint", None)
return constraint
def execute(self, context):
tgts = context.constraint.targets
tgts.remove(tgts[self.index])
@ -58,6 +68,11 @@ class CONSTRAINT_OT_normalize_target_weights(Operator):
bl_label = "Normalize Weights"
bl_options = {'UNDO', 'INTERNAL'}
@classmethod
def poll(cls, context):
constraint = getattr(context, "constraint", None)
return constraint
def execute(self, context):
tgts = context.constraint.targets
total = sum(t.weight for t in tgts)

View File

@ -42,8 +42,8 @@ def geometry_node_group_empty_new():
def geometry_modifier_poll(context):
ob = context.object
# Test object support for geometry node modifier (No volume, curve, or hair object support yet)
if not ob or ob.type not in {'MESH', 'POINTCLOUD'}:
# Test object support for geometry node modifier (No curve, or hair object support yet)
if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME'}:
return False
return True

View File

@ -120,7 +120,7 @@ class NodeAddOperator:
def poll(cls, context):
space = context.space_data
# needs active node editor and a tree to add nodes to
return ((space.type == 'NODE_EDITOR') and
return (space and (space.type == 'NODE_EDITOR') and
space.edit_tree and not space.edit_tree.library)
# Default execute simply adds a node
@ -265,7 +265,7 @@ class NODE_OT_collapse_hide_unused_toggle(Operator):
def poll(cls, context):
space = context.space_data
# needs active node editor and a tree
return ((space.type == 'NODE_EDITOR') and
return (space and (space.type == 'NODE_EDITOR') and
(space.edit_tree and not space.edit_tree.library))
def execute(self, context):
@ -296,7 +296,7 @@ class NODE_OT_tree_path_parent(Operator):
def poll(cls, context):
space = context.space_data
# needs active node editor and a tree
return (space.type == 'NODE_EDITOR' and len(space.path) > 1)
return (space and (space.type == 'NODE_EDITOR') and len(space.path) > 1)
def execute(self, context):
space = context.space_data
@ -315,6 +315,8 @@ class NODE_OT_active_preview_toggle(Operator):
@classmethod
def poll(cls, context):
space = context.space_data
if space is None:
return False
if space.type != 'NODE_EDITOR':
return False
if space.edit_tree is None:

View File

@ -133,7 +133,7 @@ class SelectCamera(Operator):
scene = context.scene
view_layer = context.view_layer
view = context.space_data
if view.type == 'VIEW_3D' and view.use_local_camera:
if view and view.type == 'VIEW_3D' and view.use_local_camera:
camera = view.camera
else:
camera = scene.camera

View File

@ -208,7 +208,8 @@ class VIEW3D_OT_transform_gizmo_set(Operator):
@classmethod
def poll(cls, context):
return context.area.type == 'VIEW_3D'
area = context.area
return area and (area.type == 'VIEW_3D')
def execute(self, context):
space_data = context.space_data

View File

@ -267,6 +267,7 @@ class DATA_PT_pathanim(CurveButtonsPanelCurve, Panel):
# these are for paths only
col.separator()
col.prop(curve, "use_path_clamp")
col.prop(curve, "use_path_follow")

View File

@ -857,6 +857,10 @@ class GreasePencilLayerRelationsPanel:
col = layout.row(align=True)
col.prop_search(gpl, "viewlayer_render", scene, "view_layers", text="View Layer")
col = layout.row(align=True)
# Only enable this property when a view layer is selected.
col.enabled = bool(gpl.viewlayer_render)
col.prop(gpl, "use_viewlayer_masks")
class GreasePencilLayerDisplayPanel:
@ -887,37 +891,43 @@ class GreasePencilFlipTintColors(Operator):
bl_idname = "gpencil.tint_flip"
bl_description = "Switch tint colors"
@classmethod
def poll(cls, context):
ts = context.tool_settings
settings = None
if context.mode == 'PAINT_GPENCIL':
settings = ts.gpencil_paint
if context.mode == 'SCULPT_GPENCIL':
settings = ts.gpencil_sculpt_paint
elif context.mode == 'WEIGHT_GPENCIL':
settings = ts.gpencil_weight_paint
elif context.mode == 'VERTEX_GPENCIL':
settings = ts.gpencil_vertex_paint
return settings and settings.brush
def execute(self, context):
try:
ts = context.tool_settings
settings = None
if context.mode == 'PAINT_GPENCIL':
settings = ts.gpencil_paint
if context.mode == 'SCULPT_GPENCIL':
settings = ts.gpencil_sculpt_paint
elif context.mode == 'WEIGHT_GPENCIL':
settings = ts.gpencil_weight_paint
elif context.mode == 'VERTEX_GPENCIL':
settings = ts.gpencil_vertex_paint
ts = context.tool_settings
settings = None
if context.mode == 'PAINT_GPENCIL':
settings = ts.gpencil_paint
if context.mode == 'SCULPT_GPENCIL':
settings = ts.gpencil_sculpt_paint
elif context.mode == 'WEIGHT_GPENCIL':
settings = ts.gpencil_weight_paint
elif context.mode == 'VERTEX_GPENCIL':
settings = ts.gpencil_vertex_paint
brush = settings.brush
if brush is not None:
color = brush.color
secondary_color = brush.secondary_color
brush = settings.brush
color = brush.color
secondary_color = brush.secondary_color
orig_prim = color.hsv
orig_sec = secondary_color.hsv
orig_prim = color.hsv
orig_sec = secondary_color.hsv
color.hsv = orig_sec
secondary_color.hsv = orig_prim
return {'FINISHED'}
except Exception as e:
utils_core.error_handlers(self, "gpencil.tint_flip", e,
"Flip Colors could not be completed")
return {'CANCELLED'}
color.hsv = orig_sec
secondary_color.hsv = orig_prim
return {'FINISHED'}
classes = (

View File

@ -1803,9 +1803,10 @@ class PARTICLE_PT_force_fields_type1(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
col = layout.column()
col.prop(part.force_field_1, "type", text="Type 1")
basic_force_field_settings_ui(self, part.force_field_1)
if part.force_field_1:
col = layout.column()
col.prop(part.force_field_1, "type", text="Type 1")
basic_force_field_settings_ui(self, part.force_field_1)
class PARTICLE_PT_force_fields_type2(ParticleButtonsPanel, Panel):
@ -1819,9 +1820,10 @@ class PARTICLE_PT_force_fields_type2(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
col = layout.column()
col.prop(part.force_field_2, "type", text="Type 2")
basic_force_field_settings_ui(self, part.force_field_2)
if part.force_field_2:
col = layout.column()
col.prop(part.force_field_2, "type", text="Type 2")
basic_force_field_settings_ui(self, part.force_field_2)
class PARTICLE_PT_force_fields_type1_falloff(ParticleButtonsPanel, Panel):
@ -1836,7 +1838,8 @@ class PARTICLE_PT_force_fields_type1_falloff(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
basic_force_field_falloff_ui(self, part.force_field_1)
if part.force_field_1:
basic_force_field_falloff_ui(self, part.force_field_1)
class PARTICLE_PT_force_fields_type2_falloff(ParticleButtonsPanel, Panel):
@ -1851,7 +1854,8 @@ class PARTICLE_PT_force_fields_type2_falloff(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
basic_force_field_falloff_ui(self, part.force_field_2)
if part.force_field_2:
basic_force_field_falloff_ui(self, part.force_field_2)
class PARTICLE_PT_vertexgroups(ParticleButtonsPanel, Panel):

View File

@ -79,7 +79,7 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
col = flow.column()
if obj.field.type == 'NONE':
if not obj.field or obj.field.type == 'NONE':
col.operator("object.forcefield_toggle", text="Force Field", icon='FORCE_FORCE')
else:
col.operator("object.forcefield_toggle", text="Force Field", icon='X')

View File

@ -1453,7 +1453,7 @@ class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
if strip.scene_input == 'CAMERA':
layout = layout.column(heading="Show")
layout.prop(strip, "use_grease_pencil", text="Grease Pencil")
layout.prop(strip, "use_annotations", text="Annotations")
if scene:
# Warning, this is not a good convention to follow.
# Expose here because setting the alpha from the 'Render' menu is very inconvenient.

View File

@ -2314,6 +2314,7 @@ class VIEW3D_MT_object_animation(Menu):
layout.operator("nla.bake", text="Bake Action...")
layout.operator("gpencil.bake_mesh_animation", text="Bake Mesh to Grease Pencil...")
layout.operator("gpencil.bake_grease_pencil_animation", text="Bake Object Transform to Grease Pencil...")
class VIEW3D_MT_object_rigid_body(Menu):
@ -6189,6 +6190,12 @@ class VIEW3D_PT_overlay_geometry(Panel):
sub.prop(overlay, "wireframe_opacity", text="Opacity")
row = col.row(align=True)
# These properties should be always available in the UI for all modes
# other than Object.
# Even when the Fade Inactive Geometry overlay is not affecting the
# current active object depending on its mode, it will always affect
# the rest of the scene.
if context.mode != 'OBJECT':
row.prop(overlay, "show_fade_inactive", text="")
sub = row.row()

View File

@ -472,14 +472,6 @@ texture_node_categories = [
]),
]
def not_implemented_node(idname):
NodeType = getattr(bpy.types, idname)
name = NodeType.bl_rna.name
label = "%s (mockup)" % name
return NodeItem(idname, label=label)
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
@ -503,6 +495,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeTransfer"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeRGBCurve"),
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
@ -523,8 +516,13 @@ geometry_node_categories = [
NodeItem("ShaderNodeValue"),
NodeItem("FunctionNodeInputString"),
NodeItem("FunctionNodeInputVector"),
NodeItem("GeometryNodeInputMaterial"),
NodeItem("GeometryNodeIsViewport"),
]),
GeometryNodeCategory("GEO_MATERIAL", "Material", items=[
NodeItem("GeometryNodeMaterialAssign"),
NodeItem("GeometryNodeMaterialReplace"),
]),
GeometryNodeCategory("GEO_MESH", "Mesh", items=[
NodeItem("GeometryNodeBoolean"),
NodeItem("GeometryNodeTriangulate"),
@ -560,6 +558,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeSwitch"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeVectorCurve"),
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeVectorMath"),

View File

@ -1,4 +1,4 @@
# Example of a custom widget that defines it's own geometry.
# Example of a custom widget that defines its own geometry.
#
# Usage: Select a light in the 3D view and drag the arrow at it's rear
# to change it's energy value.

View File

@ -37,10 +37,15 @@
struct AttributeMetaData {
AttributeDomain domain;
CustomDataType data_type;
constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
{
return (a.domain == b.domain) && (a.data_type == b.data_type);
}
};
/**
* Base class for the attribute intializer types described below.
* Base class for the attribute initializer types described below.
*/
struct AttributeInit {
enum class Type {
@ -305,4 +310,36 @@ template<typename T> class OutputAttribute_Typed {
}
};
/**
* A basic container around DNA CustomData so that its users
* don't have to implement special copy and move constructors.
*/
class CustomDataAttributes {
/**
* #CustomData needs a size to be freed, and unfortunately it isn't stored in the struct
* itself, so keep track of the size here so this class can implement its own destructor.
* If the implementation of the attribute storage changes, this could be removed.
*/
int size_;
public:
CustomData data;
CustomDataAttributes();
~CustomDataAttributes();
CustomDataAttributes(const CustomDataAttributes &other);
CustomDataAttributes(CustomDataAttributes &&other);
void reallocate(const int size);
std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const;
std::optional<blender::fn::GMutableSpan> get_for_write(const blender::StringRef name);
bool create(const blender::StringRef name, const CustomDataType data_type);
bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
bool remove(const blender::StringRef name);
bool foreach_attribute(const AttributeForeachCallback callback,
const AttributeDomain domain) const;
};
} // namespace blender::bke

View File

@ -52,7 +52,7 @@ inline void convert_to_static_type(const CustomDataType data_type, const Func &f
func(bool());
break;
case CD_PROP_COLOR:
func(Color4f());
func(ColorGeometry4f());
break;
default:
BLI_assert_unreachable();
@ -78,8 +78,8 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func
else if (cpp_type.is<bool>()) {
func(bool());
}
else if (cpp_type.is<Color4f>()) {
func(Color4f());
else if (cpp_type.is<ColorGeometry4f>()) {
func(ColorGeometry4f());
}
else {
BLI_assert_unreachable();
@ -123,9 +123,12 @@ inline float3 mix3(const float3 &weights, const float3 &v0, const float3 &v1, co
}
template<>
inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1, const Color4f &v2)
inline ColorGeometry4f mix3(const float3 &weights,
const ColorGeometry4f &v0,
const ColorGeometry4f &v1,
const ColorGeometry4f &v2)
{
Color4f result;
ColorGeometry4f result;
interp_v4_v4v4v4(result, v0, v1, v2, weights);
return result;
}
@ -165,9 +168,10 @@ template<> inline float3 mix2(const float factor, const float3 &a, const float3
return float3::interpolate(a, b, factor);
}
template<> inline Color4f mix2(const float factor, const Color4f &a, const Color4f &b)
template<>
inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b)
{
Color4f result;
ColorGeometry4f result;
interp_v4_v4v4(result, a, b, factor);
return result;
}
@ -274,15 +278,16 @@ class SimpleMixerWithAccumulationType {
}
};
class Color4fMixer {
class ColorGeometryMixer {
private:
MutableSpan<Color4f> buffer_;
Color4f default_color_;
MutableSpan<ColorGeometry4f> buffer_;
ColorGeometry4f default_color_;
Array<float> total_weights_;
public:
Color4fMixer(MutableSpan<Color4f> buffer, Color4f default_color = {0, 0, 0, 1});
void mix_in(const int64_t index, const Color4f &color, const float weight = 1.0f);
ColorGeometryMixer(MutableSpan<ColorGeometry4f> buffer,
ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
void mix_in(const int64_t index, const ColorGeometry4f &color, const float weight = 1.0f);
void finalize();
};
@ -299,10 +304,10 @@ template<> struct DefaultMixerStruct<float2> {
template<> struct DefaultMixerStruct<float3> {
using type = SimpleMixer<float3>;
};
template<> struct DefaultMixerStruct<Color4f> {
/* Use a special mixer for colors. Color4f can't be added/multiplied, because this is not
template<> struct DefaultMixerStruct<ColorGeometry4f> {
/* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not
* something one should usually do with colors. */
using type = Color4fMixer;
using type = ColorGeometryMixer;
};
template<> struct DefaultMixerStruct<int> {
static int double_to_int(const double &value)

View File

@ -420,6 +420,10 @@ class CurveComponent : public GeometryComponent {
CurveEval *get_for_write();
int attribute_domain_size(const AttributeDomain domain) const final;
std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
std::unique_ptr<blender::fn::GVArray> varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const final;
bool is_empty() const final;

View File

@ -111,7 +111,10 @@ void BKE_gpencil_dissolve_points(struct bGPdata *gpd,
struct bGPDstroke *gps,
const short tag);
bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float tip_length);
bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps,
const float dist,
const float overshoot_fac,
const short mode);
bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps,
const int index_from,
const int index_to);
@ -135,7 +138,7 @@ bool BKE_gpencil_stroke_split(struct bGPdata *gpd,
struct bGPDstroke *gps,
const int before_index,
struct bGPDstroke **remaining_gps);
bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist);
bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist, const short mode);
float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d);
float BKE_gpencil_stroke_segment_length(const struct bGPDstroke *gps,

View File

@ -104,6 +104,10 @@ enum {
* specific code in some copy cases (mostly for node trees). */
LIB_ID_CREATE_LOCAL = 1 << 9,
/** Create for the depsgraph, when set #LIB_TAG_COPIED_ON_WRITE must be set.
* Internally this is used to share some pointers instead of duplicating them. */
LIB_ID_COPY_SET_COPIED_ON_WRITE = 1 << 10,
/* *** Specific options to some ID types or usages. *** */
/* *** May be ignored by unrelated ID copying functions. *** */
/** Object only, needed by make_local code. */

View File

@ -47,6 +47,7 @@ struct ID;
struct IDOverrideLibrary;
struct IDOverrideLibraryProperty;
struct IDOverrideLibraryPropertyOperation;
struct Library;
struct Main;
struct Object;
struct PointerRNA;
@ -68,7 +69,9 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id);
struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
struct ID *reference_id,
const bool do_tagged_remap);
bool BKE_lib_override_library_create_from_tag(struct Main *bmain);
bool BKE_lib_override_library_create_from_tag(struct Main *bmain,
const struct Library *reference_library,
const bool do_no_main);
bool BKE_lib_override_library_create(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,

View File

@ -85,6 +85,10 @@ enum {
* freed ones).
*/
ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS = 1 << 7,
/** Force handling user count even for IDs that are outside of Main (used in some cases when
* dealing with IDs temporarily out of Main, but which will be put in it ultimately).
*/
ID_REMAP_FORCE_USER_REFCOUNT = 1 << 8,
};
/* Note: Requiring new_id to be non-null, this *may* not be the case ultimately,

View File

@ -51,6 +51,9 @@ void BKE_object_material_remap(struct Object *ob, const unsigned int *remap);
void BKE_object_material_remap_calc(struct Object *ob_dst,
struct Object *ob_src,
short *remap_src_to_dst);
void BKE_object_material_from_eval_data(struct Main *bmain,
struct Object *ob_orig,
struct ID *data_eval);
struct Material *BKE_material_add(struct Main *bmain, const char *name);
struct Material *BKE_gpencil_material_add(struct Main *bmain, const char *name);
void BKE_gpencil_material_attr_init(struct Material *ma);
@ -105,6 +108,12 @@ struct Material *BKE_id_material_pop(struct Main *bmain,
/* index is an int because of RNA. */
int index);
void BKE_id_material_clear(struct Main *bmain, struct ID *id);
/* eval api */
struct Material *BKE_object_material_get_eval(struct Object *ob, short act);
int BKE_object_material_count_eval(struct Object *ob);
void BKE_id_material_eval_assign(struct ID *id, int slot, struct Material *material);
/* rendering */
void ramp_blend(int type, float r_col[3], const float fac, const float col[3]);

View File

@ -325,6 +325,7 @@ typedef struct bNodeType {
/* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute;
bool geometry_node_execute_supports_laziness;
/* RNA integration */
ExtensionRNA rna_ext;
@ -1422,6 +1423,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_ATTRIBUTE_CURVE_MAP 1046
#define GEO_NODE_CURVE_RESAMPLE 1047
#define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048
#define GEO_NODE_MATERIAL_ASSIGN 1049
#define GEO_NODE_INPUT_MATERIAL 1050
#define GEO_NODE_MATERIAL_REPLACE 1051
/** \} */

View File

@ -95,21 +95,13 @@ struct AvailableAttributeInfo {
};
struct NodeUIStorage {
std::mutex mutex;
blender::Vector<NodeWarning> warnings;
blender::Set<AvailableAttributeInfo> attribute_hints;
NodeUIStorage() = default;
/* Needed because the mutex can't be moved or copied. */
NodeUIStorage(NodeUIStorage &&other)
: warnings(std::move(other.warnings)), attribute_hints(std::move(other.attribute_hints))
{
}
};
struct NodeTreeUIStorage {
std::mutex mutex;
blender::Map<NodeTreeEvaluationContext, blender::Map<std::string, NodeUIStorage>> context_map;
std::mutex context_map_mutex;
/**
* Attribute search uses this to store the fake info for the string typed into a node, in order

View File

@ -28,6 +28,7 @@
#include "BLI_float4x4.hh"
#include "BLI_vector.hh"
#include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh"
struct Curve;
@ -74,6 +75,8 @@ class Spline {
/* Only #Zup is supported at the moment. */
NormalCalculationMode normal_mode;
blender::bke::CustomDataAttributes attributes;
protected:
Type type_;
bool is_cyclic_ = false;
@ -99,7 +102,10 @@ class Spline {
{
}
Spline(Spline &other)
: normal_mode(other.normal_mode), type_(other.type_), is_cyclic_(other.is_cyclic_)
: normal_mode(other.normal_mode),
attributes(other.attributes),
type_(other.type_),
is_cyclic_(other.is_cyclic_)
{
}
@ -482,20 +488,30 @@ class CurveEval {
blender::Vector<SplinePtr> splines_;
public:
blender::bke::CustomDataAttributes attributes;
CurveEval() = default;
CurveEval(const CurveEval &other) : attributes(other.attributes)
{
for (const SplinePtr &spline : other.splines()) {
this->add_spline(spline->copy());
}
}
blender::Span<SplinePtr> splines() const;
blender::MutableSpan<SplinePtr> splines();
void add_spline(SplinePtr spline);
void remove_splines(blender::IndexMask mask);
CurveEval *copy();
void translate(const blender::float3 &translation);
void transform(const blender::float4x4 &matrix);
void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
blender::Array<int> control_point_offsets() const;
blender::Array<int> evaluated_point_offsets() const;
void assert_valid_point_attributes() const;
};
std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve);

View File

@ -769,6 +769,7 @@ if(WITH_GTESTS)
intern/fcurve_test.cc
intern/lattice_deform_test.cc
intern/layer_test.cc
intern/lib_id_test.cc
intern/tracking_test.cc
)
set(TEST_INC

View File

@ -2882,7 +2882,7 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden
if (bb_custom) {
float mat[4][4], smat[4][4];
scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan));
mul_m4_v3(smat, pchan->custom_scale_xyz);
rescale_m4(smat, pchan->custom_scale_xyz);
mul_m4_series(mat, ob->obmat, pchan_tx->pose_mat, smat);
BKE_boundbox_minmax(bb_custom, mat, r_min, r_max);
}

View File

@ -45,6 +45,7 @@ using blender::Set;
using blender::StringRef;
using blender::StringRefNull;
using blender::fn::GMutableSpan;
using blender::fn::GSpan;
namespace blender::bke {
@ -60,7 +61,7 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty
case CD_PROP_INT32:
return &CPPType::get<int>();
case CD_PROP_COLOR:
return &CPPType::get<Color4f>();
return &CPPType::get<ColorGeometry4f>();
case CD_PROP_BOOL:
return &CPPType::get<bool>();
default:
@ -83,7 +84,7 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type)
if (type.is<int>()) {
return CD_PROP_INT32;
}
if (type.is<Color4f>()) {
if (type.is<ColorGeometry4f>()) {
return CD_PROP_COLOR;
}
if (type.is<bool>()) {
@ -354,7 +355,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
case CD_PROP_INT32:
return this->layer_to_read_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
return this->layer_to_read_attribute<Color4f>(layer, domain_size);
return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_read_attribute<bool>(layer, domain_size);
default:
@ -388,7 +389,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
case CD_PROP_INT32:
return this->layer_to_write_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
return this->layer_to_write_attribute<Color4f>(layer, domain_size);
return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_write_attribute<bool>(layer, domain_size);
default:
@ -590,6 +591,105 @@ void NamedLegacyCustomDataProvider::foreach_domain(
callback(domain_);
}
CustomDataAttributes::CustomDataAttributes()
{
CustomData_reset(&data);
size_ = 0;
}
CustomDataAttributes::~CustomDataAttributes()
{
CustomData_free(&data, size_);
}
CustomDataAttributes::CustomDataAttributes(const CustomDataAttributes &other)
{
size_ = other.size_;
CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, size_);
}
CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other)
{
size_ = other.size_;
data = other.data;
CustomData_reset(&other.data);
}
std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const
{
BLI_assert(size_ != 0);
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
if (layer.name == name) {
const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
BLI_assert(cpp_type != nullptr);
return GSpan(*cpp_type, layer.data, size_);
}
}
return {};
}
std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name)
{
BLI_assert(size_ != 0);
for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
if (layer.name == name) {
const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
BLI_assert(cpp_type != nullptr);
return GMutableSpan(*cpp_type, layer.data, size_);
}
}
return {};
}
bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type)
{
char name_c[MAX_NAME];
name.copy(name_c);
void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c);
return result != nullptr;
}
bool CustomDataAttributes::create_by_move(const blender::StringRef name,
const CustomDataType data_type,
void *buffer)
{
char name_c[MAX_NAME];
name.copy(name_c);
void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c);
return result != nullptr;
}
bool CustomDataAttributes::remove(const blender::StringRef name)
{
bool result = false;
for (const int i : IndexRange(data.totlayer)) {
const CustomDataLayer &layer = data.layers[i];
if (layer.name == name) {
CustomData_free_layer(&data, layer.type, size_, i);
result = true;
}
}
return result;
}
void CustomDataAttributes::reallocate(const int size)
{
size_ = size;
CustomData_realloc(&data, size);
}
bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback callback,
const AttributeDomain domain) const
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
if (!callback(layer.name, meta_data)) {
return false;
}
}
return true;
}
} // namespace blender::bke
/* -------------------------------------------------------------------- */

View File

@ -18,18 +18,21 @@
namespace blender::attribute_math {
Color4fMixer::Color4fMixer(MutableSpan<Color4f> output_buffer, Color4f default_color)
ColorGeometryMixer::ColorGeometryMixer(MutableSpan<ColorGeometry4f> output_buffer,
ColorGeometry4f default_color)
: buffer_(output_buffer),
default_color_(default_color),
total_weights_(output_buffer.size(), 0.0f)
{
buffer_.fill(Color4f(0, 0, 0, 0));
buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float weight)
void ColorGeometryMixer::mix_in(const int64_t index,
const ColorGeometry4f &color,
const float weight)
{
BLI_assert(weight >= 0.0f);
Color4f &output_color = buffer_[index];
ColorGeometry4f &output_color = buffer_[index];
output_color.r += color.r * weight;
output_color.g += color.g * weight;
output_color.b += color.b * weight;
@ -37,11 +40,11 @@ void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float
total_weights_[index] += weight;
}
void Color4fMixer::finalize()
void ColorGeometryMixer::finalize()
{
for (const int64_t i : buffer_.index_range()) {
const float weight = total_weights_[i];
Color4f &output_color = buffer_[i];
ColorGeometry4f &output_color = buffer_[i];
if (weight > 0.0f) {
const float weight_inv = 1.0f / weight;
output_color.r *= weight_inv;

View File

@ -1473,8 +1473,12 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
* that's animated, but this will only work if it actually is animated...
*
* we divide the curvetime calculated in the previous step by the length of the path,
* to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. */
* to get a time factor. */
curvetime /= cu->pathlen;
if (cu->flag & CU_PATH_CLAMP) {
CLAMP(curvetime, 0.0f, 1.0f);
}
}
else {
/* fixed position along curve */

View File

@ -16,7 +16,9 @@
#include "BLI_array.hh"
#include "BLI_listbase.h"
#include "BLI_map.hh"
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "DNA_curve_types.h"
@ -26,7 +28,9 @@
using blender::Array;
using blender::float3;
using blender::float4x4;
using blender::Map;
using blender::Span;
using blender::StringRefNull;
blender::Span<SplinePtr> CurveEval::splines() const
{
@ -38,6 +42,9 @@ blender::MutableSpan<SplinePtr> CurveEval::splines()
return splines_;
}
/**
* \warning Call #reallocate on the spline's attributes after adding all splines.
*/
void CurveEval::add_spline(SplinePtr spline)
{
splines_.append(std::move(spline));
@ -50,17 +57,6 @@ void CurveEval::remove_splines(blender::IndexMask mask)
}
}
CurveEval *CurveEval::copy()
{
CurveEval *new_curve = new CurveEval();
for (SplinePtr &spline : this->splines()) {
new_curve->add_spline(spline->copy());
}
return new_curve;
}
void CurveEval::translate(const float3 &translation)
{
for (SplinePtr &spline : this->splines()) {
@ -189,7 +185,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
bezt.radius,
bezt.tilt);
}
spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
break;
}
@ -203,7 +199,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
}
spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
break;
}
@ -214,7 +210,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
spline->add_point(bp.vec, bp.radius, bp.tilt);
}
spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
break;
}
@ -225,6 +221,9 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
}
}
/* Though the curve has no attributes, this is necessary to properly set the custom data size. */
curve->attributes.reallocate(curve->splines().size());
/* Note: Normal mode is stored separately in each spline to facilitate combining splines
* from multiple curve objects, where the value may be different. */
const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
@ -235,3 +234,39 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
return curve;
}
/**
* Check the invariants that curve control point attributes should always uphold, necessary
* because attributes are stored on splines rather than in a flat array on the curve:
* - The same set of attributes exists on every spline.
* - Attributes with the same name have the same type on every spline.
*/
void CurveEval::assert_valid_point_attributes() const
{
#ifdef DEBUG
if (splines_.size() == 0) {
return;
}
const int layer_len = splines_.first()->attributes.data.totlayer;
Map<StringRefNull, AttributeMetaData> map;
for (const SplinePtr &spline : splines_) {
BLI_assert(spline->attributes.data.totlayer == layer_len);
spline->attributes.foreach_attribute(
[&](StringRefNull name, const AttributeMetaData &meta_data) {
map.add_or_modify(
name,
[&](AttributeMetaData *map_data) {
/* All unique attribute names should be added on the first spline. */
BLI_assert(spline == splines_.first());
*map_data = meta_data;
},
[&](AttributeMetaData *map_data) {
/* Attributes on different splines should all have the same type. */
BLI_assert(meta_data == *map_data);
});
return true;
},
ATTR_DOMAIN_POINT);
}
#endif
}

View File

@ -623,7 +623,8 @@ static void clamp_bounds_in_domain(FluidDomainSettings *fds,
static bool is_static_object(Object *ob)
{
/* Check if the object has modifiers that might make the object "dynamic". */
ModifierData *md = ob->modifiers.first;
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
for (; md; md = md->next) {
if (ELEM(md->type,
eModifierType_Cloth,
@ -631,7 +632,8 @@ static bool is_static_object(Object *ob)
eModifierType_Explode,
eModifierType_Ocean,
eModifierType_ShapeKey,
eModifierType_Softbody)) {
eModifierType_Softbody,
eModifierType_Nodes)) {
return false;
}
}

View File

@ -14,14 +14,20 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BKE_spline.hh"
#include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh"
#include "BKE_geometry_set.hh"
#include "BKE_spline.hh"
#include "attribute_access_intern.hh"
using blender::fn::GMutableSpan;
using blender::fn::GSpan;
using blender::fn::GVArray_For_GSpan;
using blender::fn::GVArray_GSpan;
using blender::fn::GVArrayPtr;
using blender::fn::GVMutableArray_For_GMutableSpan;
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
* \{ */
@ -39,7 +45,7 @@ GeometryComponent *CurveComponent::copy() const
{
CurveComponent *new_component = new CurveComponent();
if (curve_ != nullptr) {
new_component->curve_ = curve_->copy();
new_component->curve_ = new CurveEval(*curve_);
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return new_component;
@ -87,7 +93,7 @@ CurveEval *CurveComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
curve_ = curve_->copy();
curve_ = new CurveEval(*curve_);
ownership_ = GeometryOwnershipType::Owned;
}
return curve_;
@ -107,7 +113,7 @@ void CurveComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
curve_ = curve_->copy();
curve_ = new CurveEval(*curve_);
ownership_ = GeometryOwnershipType::Owned;
}
}
@ -136,6 +142,171 @@ int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
return 0;
}
namespace blender::bke {
namespace {
struct PointIndices {
int spline_index;
int point_index;
};
} // namespace
static PointIndices lookup_point_indices(Span<int> offsets, const int index)
{
const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) -
offsets.begin() - 1;
const int index_in_spline = index - offsets[spline_index];
return {spline_index, index_in_spline};
}
/**
* Mix together all of a spline's control point values.
*
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
* only some values are required.
*/
template<typename T>
static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
const VArray<T> &old_values,
MutableSpan<T> r_values)
{
const int splines_len = curve.splines().size();
Array<int> offsets = curve.control_point_offsets();
BLI_assert(r_values.size() == splines_len);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int i_spline : IndexRange(splines_len)) {
const int spline_offset = offsets[i_spline];
const int spline_point_len = offsets[i_spline + 1] - spline_offset;
for (const int i_point : IndexRange(spline_point_len)) {
const T value = old_values[spline_offset + i_point];
mixer.mix_in(i_spline, value);
}
}
mixer.finalize();
}
static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(curve.splines().size());
adapt_curve_domain_point_to_spline_impl<T>(curve, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
return new_varray;
}
/**
* A virtual array implementation for the conversion of spline attributes to control point
* attributes. The goal is to avoid copying the spline value for every one of its control points
* unless it is necessary (in that case the materialize functions will be called).
*/
template<typename T> class VArray_For_SplineToPoint final : public VArray<T> {
/* Store existing data materialized if it was not already a span. This is expected
* to be worth it because a single spline's value will likely be accessed many times. */
VArray_Span<T> original_data_;
Array<int> offsets_;
public:
VArray_For_SplineToPoint(const VArray<T> &original_varray, Array<int> offsets)
: VArray<T>(offsets.last()), original_data_(original_varray), offsets_(std::move(offsets))
{
}
T get_impl(const int64_t index) const final
{
const PointIndices indices = lookup_point_indices(offsets_, index);
return original_data_[indices.spline_index];
}
void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
{
const int total_size = offsets_.last();
if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
for (const int spline_index : original_data_.index_range()) {
const int offset = offsets_[spline_index];
const int next_offset = offsets_[spline_index + 1];
r_span.slice(offset, next_offset - offset).fill(original_data_[spline_index]);
}
}
else {
int spline_index = 0;
for (const int dst_index : mask) {
while (offsets_[spline_index] < dst_index) {
spline_index++;
}
r_span[dst_index] = original_data_[spline_index];
}
}
}
void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
{
T *dst = r_span.data();
const int total_size = offsets_.last();
if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
for (const int spline_index : original_data_.index_range()) {
const int offset = offsets_[spline_index];
const int next_offset = offsets_[spline_index + 1];
uninitialized_fill_n(dst + offset, next_offset - offset, original_data_[spline_index]);
}
}
else {
int spline_index = 0;
for (const int dst_index : mask) {
while (offsets_[spline_index] < dst_index) {
spline_index++;
}
new (dst + dst_index) T(original_data_[spline_index]);
}
}
}
};
static GVArrayPtr adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArrayPtr varray)
{
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
Array<int> offsets = curve.control_point_offsets();
new_varray = std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplineToPoint<T>>>(
offsets.last(), *varray->typed<T>(), std::move(offsets));
});
return new_varray;
}
} // namespace blender::bke
GVArrayPtr CurveComponent::attribute_try_adapt_domain(GVArrayPtr varray,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
if (!varray) {
return {};
}
if (varray->size() == 0) {
return {};
}
if (from_domain == to_domain) {
return varray;
}
if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) {
return blender::bke::adapt_curve_domain_point_to_spline(*curve_, std::move(varray));
}
if (from_domain == ATTR_DOMAIN_CURVE && to_domain == ATTR_DOMAIN_POINT) {
return blender::bke::adapt_curve_domain_spline_to_point(*curve_, std::move(varray));
}
return {};
}
static CurveEval *get_curve_from_component_for_write(GeometryComponent &component)
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
@ -294,20 +465,6 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
* array implementations try to make it workable in common situations.
* \{ */
namespace {
struct PointIndices {
int spline_index;
int point_index;
};
} // namespace
static PointIndices lookup_point_indices(Span<int> offsets, const int index)
{
const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) -
offsets.begin() - 1;
const int index_in_spline = index - offsets[spline_index];
return {spline_index, index_in_spline};
}
template<typename T>
static void point_attribute_materialize(Span<Span<T>> data,
Span<int> offsets,
@ -319,14 +476,12 @@ static void point_attribute_materialize(Span<Span<T>> data,
for (const int spline_index : data.index_range()) {
const int offset = offsets[spline_index];
const int next_offset = offsets[spline_index + 1];
initialized_copy_n(data[spline_index].data(), next_offset - offset, r_span.data() + offset);
r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]);
}
}
else {
int spline_index = 0;
for (const int i : r_span.index_range()) {
const int dst_index = mask[i];
for (const int dst_index : mask) {
while (offsets[spline_index] < dst_index) {
spline_index++;
}
@ -354,9 +509,7 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
}
else {
int spline_index = 0;
for (const int i : r_span.index_range()) {
const int dst_index = mask[i];
for (const int dst_index : mask) {
while (offsets[spline_index] < dst_index) {
spline_index++;
}
@ -445,6 +598,20 @@ template<typename T> class VMutableArray_For_SplinePoints final : public VMutabl
}
};
template<typename T> GVArrayPtr point_data_gvarray(Array<Span<T>> spans, Array<int> offsets)
{
return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
offsets.last(), std::move(spans), std::move(offsets));
}
template<typename T>
GVMutableArrayPtr point_data_gvarray(Array<MutableSpan<T>> spans, Array<int> offsets)
{
return std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
offsets.last(), std::move(spans), std::move(offsets));
}
/**
* Virtual array implementation specifically for control point positions. This is only needed for
* Bezier splines, where adjusting the position also requires adjusting handle positions depending
@ -581,8 +748,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
spans[i] = get_span_(*splines[i]);
}
return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
offsets.last(), std::move(spans), std::move(offsets));
return point_data_gvarray(spans, offsets);
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
@ -607,9 +773,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
}
}
return std::make_unique<
fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
offsets.last(), std::move(spans), std::move(offsets));
return point_data_gvarray(spans, offsets);
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
@ -662,7 +826,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
}
/* Use the regular position virtual array when there aren't any Bezier splines
* to avoid the overhead of thecking the spline type for every point. */
* to avoid the overhead of checking the spline type for every point. */
if (!curve_has_bezier_spline) {
return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
}
@ -682,6 +846,256 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
/** \} */
/* -------------------------------------------------------------------- */
/** \name Dynamic Control Point Attributes
*
* The dynamic control point attribute implementation is very similar to the builtin attribute
* implementation-- it uses the same virtual array types. In order to work, this code depends on
* the fact that all a curve's splines will have the same attributes and they all have the same
* type.
* \{ */
class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
private:
static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
public:
ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
const StringRef attribute_name) const final
{
const CurveEval *curve = get_curve_from_component_for_read(component);
if (curve == nullptr || curve->splines().size() == 0) {
return {};
}
Span<SplinePtr> splines = curve->splines();
Vector<GSpan> spans; /* GSpan has no default constructor. */
spans.reserve(splines.size());
std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_name);
if (!first_span) {
return {};
}
spans.append(*first_span);
for (const int i : IndexRange(1, splines.size() - 1)) {
std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_name);
if (!span) {
/* All splines should have the same set of data layers. It would be possible to recover
* here and return partial data instead, but that would add a lot of complexity for a
* situation we don't even expect to encounter. */
BLI_assert_unreachable();
return {};
}
if (span->type() != spans.last().type()) {
/* Data layer types on separate splines do not match. */
BLI_assert_unreachable();
return {};
}
spans.append(*span);
}
/* First check for the simpler situation when we can return a simpler span virtual array. */
if (spans.size() == 1) {
return {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
}
ReadAttributeLookup attribute = {};
Array<int> offsets = curve->control_point_offsets();
attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
using T = decltype(dummy);
Array<Span<T>> data(splines.size());
for (const int i : splines.index_range()) {
data[i] = spans[i].typed<T>();
BLI_assert(data[i].data() != nullptr);
}
attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
});
return attribute;
}
/* This function is almost the same as #try_get_for_read, but without const. */
WriteAttributeLookup try_get_for_write(GeometryComponent &component,
const StringRef attribute_name) const final
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr || curve->splines().size() == 0) {
return {};
}
MutableSpan<SplinePtr> splines = curve->splines();
Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */
spans.reserve(splines.size());
std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_name);
if (!first_span) {
return {};
}
spans.append(*first_span);
for (const int i : IndexRange(1, splines.size() - 1)) {
std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_name);
if (!span) {
/* All splines should have the same set of data layers. It would be possible to recover
* here and return partial data instead, but that would add a lot of complexity for a
* situation we don't even expect to encounter. */
BLI_assert_unreachable();
return {};
}
if (span->type() != spans.last().type()) {
/* Data layer types on separate splines do not match. */
BLI_assert_unreachable();
return {};
}
spans.append(*span);
}
/* First check for the simpler situation when we can return a simpler span virtual array. */
if (spans.size() == 1) {
return {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
}
WriteAttributeLookup attribute = {};
Array<int> offsets = curve->control_point_offsets();
attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
using T = decltype(dummy);
Array<MutableSpan<T>> data(splines.size());
for (const int i : splines.index_range()) {
data[i] = spans[i].typed<T>();
BLI_assert(data[i].data() != nullptr);
}
attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
});
return attribute;
}
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr) {
return false;
}
bool layer_freed = false;
for (SplinePtr &spline : curve->splines()) {
spline->attributes.remove(attribute_name);
}
return layer_freed;
}
static GVArrayPtr varray_from_initializer(const AttributeInit &initializer,
const CustomDataType data_type,
const int total_size)
{
switch (initializer.type) {
case AttributeInit::Type::Default:
/* This function shouldn't be called in this case, since there
* is no need to copy anything to the new custom data array. */
BLI_assert_unreachable();
return {};
case AttributeInit::Type::VArray:
return static_cast<const AttributeInitVArray &>(initializer).varray->shallow_copy();
case AttributeInit::Type::MoveArray:
return std::make_unique<fn::GVArray_For_GSpan>(
GSpan(*bke::custom_data_type_to_cpp_type(data_type),
static_cast<const AttributeInitMove &>(initializer).data,
total_size));
}
BLI_assert_unreachable();
return {};
}
bool try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer) const final
{
BLI_assert(this->type_is_supported(data_type));
if (domain != ATTR_DOMAIN_POINT) {
return false;
}
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr || curve->splines().size() == 0) {
return false;
}
MutableSpan<SplinePtr> splines = curve->splines();
/* First check the one case that allows us to avoid copying the input data. */
if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) {
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
if (!splines[0]->attributes.create_by_move(attribute_name, data_type, source_data)) {
MEM_freeN(source_data);
return false;
}
return true;
}
/* Otherwise just create a custom data layer on each of the splines. */
for (const int i : splines.index_range()) {
if (!splines[i]->attributes.create(attribute_name, data_type)) {
/* If attribute creation fails on one of the splines, we cannot leave the custom data
* layers in the previous splines around, so delete them before returning. However,
* this is not an expected case. */
BLI_assert_unreachable();
return false;
}
}
/* With a default initializer type, we can keep the values at their initial values. */
if (initializer.type == AttributeInit::Type::Default) {
return true;
}
WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name);
/* We just created the attribute, it should exist. */
BLI_assert(write_attribute);
const int total_size = curve->control_point_offsets().last();
GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
/* TODO: When we can call a variant of #set_all with a virtual array argument,
* this theoretically unnecessary materialize step could be removed. */
GVArray_GSpan source_varray_span{*source_varray};
write_attribute.varray->set_all(source_varray_span.data());
if (initializer.type == AttributeInit::Type::MoveArray) {
MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
}
return true;
}
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final
{
const CurveEval *curve = get_curve_from_component_for_read(component);
if (curve == nullptr || curve->splines().size() == 0) {
return false;
}
Span<SplinePtr> splines = curve->splines();
/* In a debug build, check that all corresponding custom data layers have the same type. */
curve->assert_valid_point_attributes();
/* Use the first spline as a representative for all the others. */
splines.first()->attributes.foreach_attribute(callback, ATTR_DOMAIN_POINT);
return true;
}
void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final
{
callback(ATTR_DOMAIN_POINT);
}
bool type_is_supported(CustomDataType data_type) const
{
return ((1ULL << data_type) & supported_types_mask) != 0;
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Attribute Provider Declaration
* \{ */
@ -704,6 +1118,20 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
make_cyclic_read_attribute,
make_cyclic_write_attribute);
static CustomDataAccessInfo spline_custom_data_access = {
[](GeometryComponent &component) -> CustomData * {
CurveEval *curve = get_curve_from_component_for_write(component);
return curve ? &curve->attributes.data : nullptr;
},
[](const GeometryComponent &component) -> const CustomData * {
const CurveEval *curve = get_curve_from_component_for_read(component);
return curve ? &curve->attributes.data : nullptr;
},
nullptr};
static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE,
spline_custom_data_access);
static PositionAttributeProvider position;
static BuiltinPointAttributeProvider<float> radius(
@ -720,7 +1148,10 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
[](Spline &spline) { return spline.tilts(); },
[](Spline &spline) { spline.mark_cache_invalid(); });
return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic}, {});
static DynamicPointAttributeProvider point_custom_data;
return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic},
{&spline_custom_data, &point_custom_data});
}
} // namespace blender::bke

View File

@ -219,8 +219,7 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
/* We compute all interpolated values at once, because for this interpolation, one has to
@ -249,8 +248,7 @@ static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
/* It is not strictly necessary to compute the value for all corners here. Instead one could
* lazily lookup the mesh topology when a specific index accessed. This can be more efficient
@ -290,8 +288,7 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
@ -329,8 +326,7 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
@ -365,8 +361,7 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
@ -394,8 +389,7 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
@ -428,8 +422,7 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
@ -467,8 +460,7 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
@ -504,8 +496,7 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
@ -543,8 +534,7 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
@ -576,8 +566,7 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
@ -615,8 +604,7 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
@ -785,18 +773,20 @@ static void set_loop_uv(MLoopUV &uv, float2 co)
copy_v2_v2(uv.uv, co);
}
static Color4f get_loop_color(const MLoopCol &col)
static ColorGeometry4f get_loop_color(const MLoopCol &col)
{
Color4f srgb_color;
rgba_uchar_to_float(srgb_color, &col.r);
Color4f linear_color;
srgb_to_linearrgb_v4(linear_color, srgb_color);
ColorGeometry4b encoded_color = ColorGeometry4b(col.r, col.g, col.b, col.a);
ColorGeometry4f linear_color = encoded_color.decode();
return linear_color;
}
static void set_loop_color(MLoopCol &col, Color4f linear_color)
static void set_loop_color(MLoopCol &col, ColorGeometry4f linear_color)
{
linearrgb_to_srgb_uchar4(&col.r, linear_color);
ColorGeometry4b encoded_color = linear_color.encode();
col.r = encoded_color.r;
col.g = encoded_color.g;
col.b = encoded_color.b;
col.a = encoded_color.a;
}
static float get_crease(const MEdge &edge)
@ -1133,8 +1123,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_COLOR,
CD_MLOOPCOL,
corner_access,
make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>,
make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>);
make_derived_read_attribute<MLoopCol, ColorGeometry4f, get_loop_color>,
make_derived_write_attribute<MLoopCol, ColorGeometry4f, get_loop_color, set_loop_color>);
static VertexGroupsAttributeProvider vertex_groups;
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);

View File

@ -15,6 +15,7 @@
*/
#include "BKE_geometry_set_instances.hh"
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
@ -361,6 +362,8 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
int64_t cd_dirty_poly = 0;
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
VectorSet<Material *> materials;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const int tot_transforms = set_group.transforms.size();
@ -374,6 +377,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
cd_dirty_poly |= mesh.runtime.cd_dirty_poly;
cd_dirty_edge |= mesh.runtime.cd_dirty_edge;
cd_dirty_loop |= mesh.runtime.cd_dirty_loop;
for (const int slot_index : IndexRange(mesh.totcol)) {
Material *material = mesh.mat[slot_index];
materials.add(material);
}
}
if (convert_points_to_vertices && set.has_pointcloud()) {
const PointCloud &pointcloud = *set.get_pointcloud_for_read();
@ -396,6 +403,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
break;
}
}
for (const int i : IndexRange(materials.size())) {
Material *material = materials[i];
BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
}
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
@ -409,6 +420,14 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
Array<int> material_index_map(mesh.totcol);
for (const int i : IndexRange(mesh.totcol)) {
Material *material = mesh.mat[i];
const int new_material_index = materials.index_of(material);
material_index_map[i] = new_material_index;
}
for (const float4x4 &transform : set_group.transforms) {
for (const int i : IndexRange(mesh.totvert)) {
const MVert &old_vert = mesh.mvert[i];
@ -438,6 +457,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh.totcol) {
new_poly.mat_nr = material_index_map[new_poly.mat_nr];
}
else {
/* The material index was invalid before. */
new_poly.mat_nr = 0;
}
}
vert_offset += mesh.totvert;
@ -537,6 +563,17 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp
}
}
for (SplinePtr &spline : new_curve->splines()) {
/* Spline instances should have no custom attributes, since they always come
* from original objects which currently do not support custom attributes.
*
* This is only true as long as a #GeometrySet cannot be instanced directly. */
BLI_assert(spline->attributes.data.totlayer == 0);
UNUSED_VARS_NDEBUG(spline);
}
new_curve->attributes.reallocate(new_curve->splines().size());
result.replace(new_curve);
}

View File

@ -2624,6 +2624,11 @@ static bool gpencil_is_layer_mask(ViewLayer *view_layer, bGPdata *gpd, bGPDlayer
continue;
}
/* Skip if masks are disabled for this view layer. */
if (gpl->flag & GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER) {
continue;
}
LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) {
if (STREQ(gpl_mask->info, mask->name)) {
return true;
@ -2667,6 +2672,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
bGPDframe *act_gpf = gpl->actframe;
bGPDframe *sta_gpf = act_gpf;
bGPDframe *end_gpf = act_gpf ? act_gpf->next : NULL;
float prev_opacity = gpl->opacity;
if (gpl->flag & GP_LAYER_HIDE) {
continue;
@ -2682,9 +2688,12 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
* This is used only in final render and never in Viewport. */
if ((view_layer != NULL) && (gpl->viewlayername[0] != '\0') &&
(!STREQ(view_layer->name, gpl->viewlayername))) {
/* If the layer is used as mask, cannot be filtered or the masking system
* will crash because needs the mask layer in the draw pipeline. */
if (!gpencil_is_layer_mask(view_layer, gpd, gpl)) {
/* Do not skip masks when rendering the view-layer so that it can still be used to clip
* other layers. Instead set their opacity to zero. */
if (gpencil_is_layer_mask(view_layer, gpd, gpl)) {
gpl->opacity = 0.0f;
}
else {
continue;
}
}
@ -2779,6 +2788,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
if (layer_cb) {
layer_cb(gpl, act_gpf, NULL, thunk);
}
gpl->opacity = prev_opacity;
continue;
}
@ -2816,6 +2826,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
/* If layer solo mode and Paint mode, only keyframes with data are displayed. */
if (GPENCIL_PAINT_MODE(gpd) && (gpl->flag & GP_LAYER_SOLO_MODE) &&
(act_gpf->framenum != cfra)) {
gpl->opacity = prev_opacity;
continue;
}
@ -2826,6 +2837,9 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
stroke_cb(gpl, act_gpf, gps, thunk);
}
}
/* Restore the opacity in case it was overwritten (used to hide masks in render). */
gpl->opacity = prev_opacity;
}
}

View File

@ -530,14 +530,23 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
/**
* Backbone stretch similar to Freestyle.
* \param gps: Stroke to sample
* \param dist: Distance of one segment
* \param tip_length: Ignore tip jittering, set zero to use default value.
* \param gps: Stroke to sample.
* \param dist: Distance of one segment.
* \param overshoot_fac: How exact is the follow curve algorithm.
* \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End)
*/
bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length)
bool BKE_gpencil_stroke_stretch(bGPDstroke *gps,
const float dist,
const float overshoot_fac,
const short mode)
{
#define BOTH 0
#define START 1
#define END 2
bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
float threshold = (tip_length == 0 ? 0.001f : tip_length);
int i;
float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac);
if (gps->totpoints < 2 || dist < FLT_EPSILON) {
return false;
@ -547,34 +556,37 @@ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float t
second_last = &pt[gps->totpoints - 2];
next_pt = &pt[1];
float len1 = 0.0f;
float len2 = 0.0f;
if (mode == BOTH || mode == START) {
float len1 = 0.0f;
i = 1;
while (len1 < threshold && gps->totpoints > i) {
next_pt = &pt[i];
len1 = len_v3v3(&next_pt->x, &pt->x);
i++;
}
float extend1 = (len1 + dist) / len1;
float result1[3];
int i = 1;
while (len1 < threshold && gps->totpoints > i) {
next_pt = &pt[i];
len1 = len_v3v3(&next_pt->x, &pt->x);
i++;
interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
copy_v3_v3(&pt->x, result1);
}
i = 2;
while (len2 < threshold && gps->totpoints >= i) {
second_last = &pt[gps->totpoints - i];
len2 = len_v3v3(&last_pt->x, &second_last->x);
i++;
if (mode == BOTH || mode == END) {
float len2 = 0.0f;
i = 2;
while (len2 < threshold && gps->totpoints >= i) {
second_last = &pt[gps->totpoints - i];
len2 = len_v3v3(&last_pt->x, &second_last->x);
i++;
}
float extend2 = (len2 + dist) / len2;
float result2[3];
interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
copy_v3_v3(&last_pt->x, result2);
}
float extend1 = (len1 + dist) / len1;
float extend2 = (len2 + dist) / len2;
float result1[3], result2[3];
interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
copy_v3_v3(&pt->x, result1);
copy_v3_v3(&last_pt->x, result2);
return true;
}
@ -702,48 +714,64 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd,
* Shrink the stroke by length.
* \param gps: Stroke to shrink
* \param dist: delta length
* \param mode: 1->Start, 2->End
*/
bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mode)
{
#define START 1
#define END 2
bGPDspoint *pt = gps->points, *second_last;
int i;
if (gps->totpoints < 2 || dist < FLT_EPSILON) {
if (gps->totpoints < 2) {
if (gps->totpoints == 1) {
second_last = &pt[1];
if (len_v3v3(&second_last->x, &pt->x) < dist) {
BKE_gpencil_stroke_trim_points(gps, 0, 0);
return true;
}
}
return false;
}
second_last = &pt[gps->totpoints - 2];
float len1, this_len1, cut_len1;
float len2, this_len2, cut_len2;
int index_start, index_end;
float len1, cut_len1;
float len2, cut_len2;
len1 = len2 = cut_len1 = cut_len2 = 0.0f;
len1 = len2 = this_len1 = this_len2 = cut_len1 = cut_len2 = 0.0f;
i = 1;
while (len1 < dist && gps->totpoints > i - 1) {
this_len1 = len_v3v3(&pt[i].x, &pt[i + 1].x);
len1 += this_len1;
cut_len1 = len1 - dist;
i++;
int index_start = 0;
int index_end = 0;
if (mode == START) {
i = 0;
index_end = gps->totpoints - 1;
while (len1 < dist && gps->totpoints > i + 1) {
len1 += len_v3v3(&pt[i].x, &pt[i + 1].x);
cut_len1 = len1 - dist;
i++;
}
index_start = i - 1;
}
index_start = i - 2;
i = 2;
while (len2 < dist && gps->totpoints >= i) {
second_last = &pt[gps->totpoints - i];
this_len2 = len_v3v3(&second_last[1].x, &second_last->x);
len2 += this_len2;
cut_len2 = len2 - dist;
i++;
if (mode == END) {
index_start = 0;
i = 2;
while (len2 < dist && gps->totpoints >= i) {
second_last = &pt[gps->totpoints - i];
len2 += len_v3v3(&second_last[1].x, &second_last->x);
cut_len2 = len2 - dist;
i++;
}
index_end = gps->totpoints - i + 2;
}
index_end = gps->totpoints - i + 2;
if (len1 < dist || len2 < dist || index_end <= index_start) {
if (index_end <= index_start) {
index_start = index_end = 0; /* empty stroke */
}
if ((index_end == index_start + 1) && (cut_len1 + cut_len2 > 1.0f)) {
if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) {
index_start = index_end = 0; /* no length left to cut */
}
@ -753,22 +781,8 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
return false;
}
pt = gps->points;
float cut1 = cut_len1 / this_len1;
float cut2 = cut_len2 / this_len2;
float result1[3], result2[3];
interp_v3_v3v3(result1, &pt[1].x, &pt[0].x, cut1);
interp_v3_v3v3(result2, &pt[gps->totpoints - 2].x, &pt[gps->totpoints - 1].x, cut2);
copy_v3_v3(&pt[0].x, result1);
copy_v3_v3(&pt[gps->totpoints - 1].x, result2);
return true;
}
/**
* Apply smooth position to stroke point.
* \param gps: Stroke to smooth

View File

@ -105,6 +105,11 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS_ID(data, key->from, IDWALK_CB_LOOPBACK);
}
static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id)
{
return ((Key *)id)->from;
}
static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Key *key = (Key *)id;
@ -216,7 +221,9 @@ IDTypeInfo IDType_ID_KE = {
.make_local = NULL,
.foreach_id = shapekey_foreach_id,
.foreach_cache = NULL,
.owner_get = NULL, /* Could have one actually? */
/* A bit weird, due to shapekeys not being strictly speaking embedded data... But they also
* share a lot with those (non linkable, only ever used by one owner ID, etc.). */
.owner_get = shapekey_owner_get,
.blend_write = shapekey_blend_write,
.blend_read_data = shapekey_blend_read_data,

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