Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
702376e935
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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?)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -384,6 +384,7 @@ class FluidSolver : public PbClass {
|
|||
GridStorage<Real> mGrids4dReal;
|
||||
GridStorage<Vec3> mGrids4dVec;
|
||||
GridStorage<Vec4> mGrids4dVec4;
|
||||
|
||||
public:
|
||||
PbArgs _args;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
|
||||
|
||||
#define MANTA_GIT_VERSION "commit 39b7a415721ecbf6643612a24e8eadd221aeb934"
|
||||
#define MANTA_GIT_VERSION "commit 9c505cd22e289b98c9aa717efba8ef3201c7e458"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -266,6 +266,7 @@ class LevelsetGrid : public Grid<Real> {
|
|||
}
|
||||
|
||||
static Real invalidTimeValue();
|
||||
|
||||
public:
|
||||
PbArgs _args;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -154,6 +154,7 @@ class MovingObstacle : public PbClass {
|
|||
int mEmptyType;
|
||||
int mID;
|
||||
static int sIDcnt;
|
||||
|
||||
public:
|
||||
PbArgs _args;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -199,6 +199,7 @@ class TurbulenceParticleSystem : public ParticleSystem<TurbulenceParticleData> {
|
|||
|
||||
private:
|
||||
WaveletNoiseField &noise;
|
||||
|
||||
public:
|
||||
PbArgs _args;
|
||||
}
|
||||
|
|
|
@ -127,6 +127,7 @@ class VortexParticleSystem : public ParticleSystem<VortexParticleData> {
|
|||
}
|
||||
|
||||
virtual ParticleBase *clone();
|
||||
|
||||
public:
|
||||
PbArgs _args;
|
||||
}
|
||||
|
|
|
@ -240,6 +240,7 @@ class VortexSheetMesh : public Mesh {
|
|||
VorticityChannel mVorticity;
|
||||
TexCoord3Channel mTex1, mTex2;
|
||||
TurbulenceChannel mTurb;
|
||||
|
||||
public:
|
||||
PbArgs _args;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ¶ms_,
|
|||
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 ¶ms_,
|
|||
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;}
|
||||
};
|
||||
|
||||
|
|
|
@ -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/.
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue