WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 358 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
170 changed files with 1978 additions and 785 deletions
Showing only changes of commit e71556270d - Show all commits

View File

@ -722,9 +722,9 @@ set(UNIFIED_RUNTIME_FILE unified-runtime-${UNIFIED_RUNTIME_VERSION}.tar.gz)
# compiler, the versions used are taken from the following location
# https://github.com/intel/intel-graphics-compiler/releases
set(IGC_VERSION 1.0.14828.26)
set(IGC_VERSION 1.0.15468.25)
set(IGC_URI https://github.com/intel/intel-graphics-compiler/archive/refs/tags/igc-${IGC_VERSION}.tar.gz)
set(IGC_HASH acad90b3e149cf87875e6b9100152f9e7644d6cc79ed54eaf52698abdb42076c)
set(IGC_HASH c2c36af98ead4f4f6975633eaa53f45b84cb96ce48d9bfa879bebfaf12367b79)
set(IGC_HASH_TYPE SHA256)
set(IGC_FILE igc-${IGC_VERSION}.tar.gz)
@ -768,9 +768,9 @@ set(IGC_SPIRV_TOOLS_HASH 327b2dba4515646eee28c1a5fe1332891e81c8b6ff289363f52877f
set(IGC_SPIRV_TOOLS_HASH_TYPE SHA256)
set(IGC_SPIRV_TOOLS_FILE SPIR-V-Tools-${IGC_SPIRV_TOOLS_VERSION}.tar.gz)
set(IGC_SPIRV_TRANSLATOR_VERSION 23f398bf369093b1fd67459db8071ffcc6b92658)
set(IGC_SPIRV_TRANSLATOR_VERSION 7e332d0acc8ee57462d9fbedefaf411fc193fdd0)
set(IGC_SPIRV_TRANSLATOR_URI https://github.com/KhronosGroup/SPIRV-LLVM-Translator/archive/${IGC_SPIRV_TRANSLATOR_VERSION}.tar.gz)
set(IGC_SPIRV_TRANSLATOR_HASH a96447656ff6b40d9ad286524e22f4e7319d439c54136026fe72550d1162cd35)
set(IGC_SPIRV_TRANSLATOR_HASH 29aadf5fd4e64ff1d4f86446eacd6a7439efeb280478988c36314c4441072c36)
set(IGC_SPIRV_TRANSLATOR_HASH_TYPE SHA256)
set(IGC_SPIRV_TRANSLATOR_FILE SPIR-V-Translator-${IGC_SPIRV_TRANSLATOR_VERSION}.tar.gz)
@ -778,15 +778,15 @@ set(IGC_SPIRV_TRANSLATOR_FILE SPIR-V-Translator-${IGC_SPIRV_TRANSLATOR_VERSION}.
### Intel Graphics Compiler DEPS END ###
########################################
set(GMMLIB_VERSION intel-gmmlib-22.3.0)
set(GMMLIB_VERSION intel-gmmlib-22.3.11)
set(GMMLIB_URI https://github.com/intel/gmmlib/archive/refs/tags/${GMMLIB_VERSION}.tar.gz)
set(GMMLIB_HASH c1f33e1519edfc527127baeb0436b783430dfd256c643130169a3a71dc86aff9)
set(GMMLIB_HASH b97f4e501c1e902a559cbd6597c008a700f4ab8c495680bf1968db99c6547afe)
set(GMMLIB_HASH_TYPE SHA256)
set(GMMLIB_FILE ${GMMLIB_VERSION}.tar.gz)
set(OCLOC_VERSION 23.30.26918.47)
set(OCLOC_VERSION 23.43.27642.40)
set(OCLOC_URI https://github.com/intel/compute-runtime/archive/refs/tags/${OCLOC_VERSION}.tar.gz)
set(OCLOC_HASH 9890f29cbf27ce7eb845f3f7711fe8f3b0c4ee2164b77871fe51102548553f8f)
set(OCLOC_HASH 67d0c6f3103ff12408a628e14f7170da3e0220313e10799693d576cea7821fe2)
set(OCLOC_HASH_TYPE SHA256)
set(OCLOC_FILE ocloc-${OCLOC_VERSION}.tar.gz)

View File

@ -9,8 +9,8 @@
set -e
if [ `id -u` -ne 0 ]; then
echo "This script must be run as root"
exit 1
echo "This script must be run as root"
exit 1
fi
# Required by: config manager command below to enable powertools.
@ -47,8 +47,6 @@ PACKAGES_FOR_LIBS=(
# Used to checkout Blender's code.
git
git-lfs
# Used to checkout Blender's `../lib/` directory.
subversion
# Used to extract packages.
bzip2
# Used to extract packages.

View File

@ -1,5 +1,5 @@
Project: Audaspace
URL: https://github.com/audaspace/audaspace
License: Apache 2.0
Upstream version: 1.4+ (0d18fe7, 2024 Jan 2)
Local modifications: JOSResampleReader default quality set to MEDIUM
Upstream version: 1.4+ (ae29ce2, 2024 Feb 26)
Local modifications: none

View File

@ -560,7 +560,7 @@ AUD_API AUD_Sound* AUD_Sound_rechannel(AUD_Sound* sound, AUD_Channels channels)
}
}
AUD_API AUD_Sound* AUD_Sound_resample(AUD_Sound* sound, AUD_SampleRate rate, bool high_quality)
AUD_API AUD_Sound* AUD_Sound_resample(AUD_Sound* sound, AUD_SampleRate rate, AUD_ResampleQuality quality)
{
assert(sound);
@ -570,10 +570,14 @@ AUD_API AUD_Sound* AUD_Sound_resample(AUD_Sound* sound, AUD_SampleRate rate, boo
specs.channels = CHANNELS_INVALID;
specs.rate = rate;
specs.format = FORMAT_INVALID;
if(high_quality)
return new AUD_Sound(new JOSResample(*sound, specs));
else
if (quality == AUD_RESAMPLE_QUALITY_FASTEST)
{
return new AUD_Sound(new LinearResample(*sound, specs));
}
else
{
return new AUD_Sound(new JOSResample(*sound, specs, static_cast<ResampleQuality>(quality)));
}
}
catch(Exception&)
{

View File

@ -300,10 +300,10 @@ extern AUD_API AUD_Sound* AUD_Sound_rechannel(AUD_Sound* sound, AUD_Channels cha
* Resamples the sound.
* \param sound The sound to resample.
* \param rate The new sample rate.
* \param high_quality When true use a higher quality but slower resampler.
* \param quality Resampling quality vs performance choice.
* \return The resampled sound.
*/
extern AUD_API AUD_Sound* AUD_Sound_resample(AUD_Sound* sound, AUD_SampleRate rate, bool high_quality);
extern AUD_API AUD_Sound* AUD_Sound_resample(AUD_Sound* sound, AUD_SampleRate rate, AUD_ResampleQuality quality);
/**
* Reverses a sound. Make sure the sound source can be reversed.

View File

@ -270,14 +270,14 @@ AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int sampl
return length;
}
AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data, char* error, size_t errorsize)
AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, AUD_ResampleQuality quality, void(*callback)(float, void*), void* data, char* error, size_t errorsize)
{
try
{
Sequence* f = dynamic_cast<Sequence *>(sound->get());
f->setSpecs(convCToSpec(specs.specs));
std::shared_ptr<IReader> reader = f->createQualityReader();
std::shared_ptr<IReader> reader = f->createQualityReader(static_cast<ResampleQuality>(quality));
reader->seek(start);
std::shared_ptr<IWriter> writer = FileWriter::createWriter(filename, convCToDSpec(specs), static_cast<Container>(format), static_cast<Codec>(codec), bitrate);
FileWriter::writeReader(reader, writer, length, buffersize, callback, data);
@ -295,7 +295,7 @@ AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int lengt
}
}
AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data, char* error, size_t errorsize)
AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, AUD_ResampleQuality quality, void(*callback)(float, void*), void* data, char* error, size_t errorsize)
{
try
{
@ -329,7 +329,7 @@ AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsign
writers.push_back(FileWriter::createWriter(stream.str(), convCToDSpec(specs), static_cast<Container>(format), static_cast<Codec>(codec), bitrate));
}
std::shared_ptr<IReader> reader = f->createQualityReader();
std::shared_ptr<IReader> reader = f->createQualityReader(static_cast<ResampleQuality>(quality));
reader->seek(start);
FileWriter::writeReader(reader, writers, length, buffersize, callback, data);
@ -346,19 +346,19 @@ AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsign
}
}
AUD_API AUD_Device* AUD_openMixdownDevice(AUD_DeviceSpecs specs, AUD_Sound* sequencer, float volume, double start)
AUD_API AUD_Device* AUD_openMixdownDevice(AUD_DeviceSpecs specs, AUD_Sound* sequencer, float volume, AUD_ResampleQuality quality, double start)
{
try
{
ReadDevice* device = new ReadDevice(convCToDSpec(specs));
device->setQuality(true);
device->setQuality(static_cast<ResampleQuality>(quality));
device->setVolume(volume);
Sequence* f = dynamic_cast<Sequence*>(sequencer->get());
f->setSpecs(convCToSpec(specs.specs));
AUD_Handle handle = device->play(f->createQualityReader());
AUD_Handle handle = device->play(f->createQualityReader(static_cast<ResampleQuality>(quality)));
if(handle.get())
{
handle->seek(start);

View File

@ -69,6 +69,7 @@ extern AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, in
* \param format The file's container format.
* \param codec The codec used for encoding the audio data.
* \param bitrate The bitrate for encoding.
* \param quality The resampling quality.
* \param callback A callback function that is called periodically during mixdown, reporting progress if length > 0. Can be NULL.
* \param data Pass through parameter that is passed to the callback.
* \param error String buffer to copy the error message to in case of failure.
@ -78,7 +79,7 @@ extern AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, in
extern AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length,
unsigned int buffersize, const char* filename,
AUD_DeviceSpecs specs, AUD_Container format,
AUD_Codec codec, unsigned int bitrate,
AUD_Codec codec, unsigned int bitrate, AUD_ResampleQuality quality,
void(*callback)(float, void*), void* data, char* error, size_t errorsize);
/**
@ -92,6 +93,7 @@ extern AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned in
* \param format The file's container format.
* \param codec The codec used for encoding the audio data.
* \param bitrate The bitrate for encoding.
* \param quality The resampling quality.
* \param callback A callback function that is called periodically during mixdown, reporting progress if length > 0. Can be NULL.
* \param data Pass through parameter that is passed to the callback.
* \param error String buffer to copy the error message to in case of failure.
@ -101,7 +103,7 @@ extern AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned in
extern AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length,
unsigned int buffersize, const char* filename,
AUD_DeviceSpecs specs, AUD_Container format,
AUD_Codec codec, unsigned int bitrate,
AUD_Codec codec, unsigned int bitrate, AUD_ResampleQuality quality,
void(*callback)(float, void*), void* data, char* error, size_t errorsize);
/**
@ -109,10 +111,12 @@ extern AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start,
* \param specs Output audio specifications.
* \param sequencer The sound scene to mix down.
* \param volume The overall mixdown volume.
* \param quality The resampling quality.
* \param start The start time of the mixdown in the sound scene.
* \return The read device for the mixdown.
*/
extern AUD_API AUD_Device* AUD_openMixdownDevice(AUD_DeviceSpecs specs, AUD_Sound* sequencer, float volume, double start);
extern AUD_API AUD_Device* AUD_openMixdownDevice(AUD_DeviceSpecs specs, AUD_Sound* sequencer,
float volume, AUD_ResampleQuality quality, double start);
/**
* Initializes audio routines (FFMPEG/JACK if it is enabled).

View File

@ -119,6 +119,15 @@ typedef enum
AUD_CHANNELS_SURROUND71 = 8 /// 7.1 surround sound.
} AUD_Channels;
/// Resampling algorithm and quality.
typedef enum
{
AUD_RESAMPLE_QUALITY_FASTEST = 0, /// Linear resample, very fast but lowest quality.
AUD_RESAMPLE_QUALITY_LOW = 1, /// JOS resample at low quality preset.
AUD_RESAMPLE_QUALITY_MEDIUM = 2, /// JOS resample at medium quality preset.
AUD_RESAMPLE_QUALITY_HIGH = 3 /// JOS resample at high quality preset.
} AUD_ResampleQuality;
/**
* The sample rate tells how many samples are played back within one second.
* Some exotic formats may use other sample rates than provided here.

View File

@ -1269,12 +1269,12 @@ Sound_rechannel(Sound* self, PyObject* args)
}
PyDoc_STRVAR(M_aud_Sound_resample_doc,
".. method:: resample(rate, high_quality)\n\n"
".. method:: resample(rate, quality)\n\n"
" Resamples the sound.\n\n"
" :arg rate: The new sample rate.\n"
" :type rate: double\n"
" :arg high_quality: When true use a higher quality but slower resampler.\n"
" :type high_quality: bool\n"
" :arg quality: Resampler performance vs quality choice (0=fastest, 3=slowest).\n"
" :type quality: int\n"
" :return: The created :class:`Sound` object.\n"
" :rtype: :class:`Sound`");
@ -1282,20 +1282,11 @@ static PyObject *
Sound_resample(Sound* self, PyObject* args)
{
double rate;
PyObject* high_qualityo;
bool high_quality = false;
int quality = 0;
if(!PyArg_ParseTuple(args, "d|O:resample", &rate, &high_qualityo))
if(!PyArg_ParseTuple(args, "d|i:resample", &rate, &quality))
return nullptr;
if(!PyBool_Check(high_qualityo))
{
PyErr_SetString(PyExc_TypeError, "high_quality is not a boolean!");
return nullptr;
}
high_quality = high_qualityo == Py_True;
PyTypeObject* type = Py_TYPE(self);
Sound* parent = (Sound*)type->tp_alloc(type, 0);
@ -1307,10 +1298,10 @@ Sound_resample(Sound* self, PyObject* args)
specs.channels = CHANNELS_INVALID;
specs.rate = rate;
specs.format = FORMAT_INVALID;
if(high_quality)
parent->sound = new std::shared_ptr<ISound>(new JOSResample(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), specs));
else
if (quality == int(ResampleQuality::FASTEST))
parent->sound = new std::shared_ptr<ISound>(new LinearResample(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), specs));
else
parent->sound = new std::shared_ptr<ISound>(new JOSResample(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), specs, static_cast<ResampleQuality>(quality)));
}
catch(Exception& e)
{

View File

@ -69,7 +69,7 @@ protected:
* @param file The source code file in which the exception was thrown.
* @param line The source code line from which the exception was thrown.
*/
Exception(std::string message, std::string file, int line);
Exception(const std::string &message, const std::string &file, int line);
public:
/**
* Destroys the object.
@ -120,7 +120,7 @@ public:
* @param file The source code file in which the exception was thrown.
* @param line The source code line from which the exception was thrown.
*/
FileException(std::string message, std::string file, int line);
FileException(const std::string &message, const std::string &file, int line);
/**
* Copy constructor.
@ -145,7 +145,7 @@ public:
* @param file The source code file in which the exception was thrown.
* @param line The source code line from which the exception was thrown.
*/
DeviceException(std::string message, std::string file, int line);
DeviceException(const std::string &message, const std::string &file, int line);
/**
* Copy constructor.
@ -171,7 +171,7 @@ public:
* @param file The source code file in which the exception was thrown.
* @param line The source code line from which the exception was thrown.
*/
StateException(std::string message, std::string file, int line);
StateException(const std::string &message, const std::string &file, int line);
/**
* Copy constructor.

View File

@ -62,14 +62,14 @@ public:
* @param name A representative name for the device.
* @param factory The factory that creates the device.
*/
static void registerDevice(std::string name, std::shared_ptr<IDeviceFactory> factory);
static void registerDevice(const std::string &name, std::shared_ptr<IDeviceFactory> factory);
/**
* Returns the factory for a specific device.
* @param name The representative name of the device.
* @return The factory if it was found, or nullptr otherwise.
*/
static std::shared_ptr<IDeviceFactory> getDeviceFactory(std::string name);
static std::shared_ptr<IDeviceFactory> getDeviceFactory(const std::string &name);
/**
* Returns the default device based on the priorities of the registered factories.
@ -92,7 +92,7 @@ public:
* If a device is currently being handled it will be released.
* @param name The representative name of the device.
*/
static void openDevice(std::string name);
static void openDevice(const std::string &name);
/**
* Opens the default device which will then be handled by the manager.

View File

@ -71,7 +71,7 @@ public:
* Sets a name for the device.
* \param name The internal name for the device.
*/
virtual void setName(std::string name)=0;
virtual void setName(const std::string &name)=0;
};
AUD_NAMESPACE_END

View File

@ -231,9 +231,9 @@ protected:
std::shared_ptr<Mixer> m_mixer;
/**
* Whether to do high or low quality resampling.
* Resampling quality.
*/
bool m_quality;
ResampleQuality m_quality;
/**
* Initializes member variables.
@ -347,9 +347,9 @@ public:
/**
* Sets the resampling quality.
* \param quality Low (false) or high (true) quality.
* \param quality Resampling quality vs performance setting.
*/
void setQuality(bool quality);
void setQuality(ResampleQuality quality);
virtual DeviceSpecs getSpecs() const;
virtual std::shared_ptr<IHandle> play(std::shared_ptr<IReader> reader, bool keep = false);

View File

@ -69,7 +69,7 @@ public:
* \param filename The sound file path.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
*/
File(std::string filename, int stream = 0);
File(const std::string &filename, int stream = 0);
/**
* Creates a new sound.

View File

@ -72,7 +72,7 @@ public:
* @return The reader created.
* @exception Exception If no file input can read the file an exception is thrown.
*/
static std::shared_ptr<IReader> createReader(std::string filename, int stream = 0);
static std::shared_ptr<IReader> createReader(const std::string &filename, int stream = 0);
/**
* Creates a file reader for the given buffer if a registed IFileInput is able to read it.
@ -89,7 +89,7 @@ public:
* \return A vector with as many streams as there are in the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
static std::vector<StreamInfo> queryStreams(std::string filename);
static std::vector<StreamInfo> queryStreams(const std::string &filename);
/**
* Queries the streams of a sound file.
@ -110,7 +110,7 @@ public:
* @return A writer that creates the file.
* @exception Exception If no file output can write the file with the given specification an exception is thrown.
*/
static std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
static std::shared_ptr<IWriter> createWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
};
AUD_NAMESPACE_END

View File

@ -54,7 +54,7 @@ public:
* \param bitrate The bitrate for encoding.
* \return The writer to write data to.
*/
static std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
static std::shared_ptr<IWriter> createWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
/**
* Writes a reader to a writer.

View File

@ -54,7 +54,7 @@ public:
* \return The reader that reads the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
virtual std::shared_ptr<IReader> createReader(std::string filename, int stream = 0)=0;
virtual std::shared_ptr<IReader> createReader(const std::string &filename, int stream = 0)=0;
/**
* Creates a reader for a file to be read from memory.
@ -71,7 +71,7 @@ public:
* \return A vector with as many streams as there are in the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
virtual std::vector<StreamInfo> queryStreams(std::string filename)=0;
virtual std::vector<StreamInfo> queryStreams(const std::string &filename)=0;
/**
* Queries the streams of a sound file.

View File

@ -46,7 +46,7 @@ public:
* \param bitrate The bitrate for encoding.
* \exception Exception Thrown if the file specified cannot be written.
*/
virtual std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)=0;
virtual std::shared_ptr<IWriter> createWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)=0;
};
AUD_NAMESPACE_END

View File

@ -36,13 +36,15 @@ private:
JOSResample(const JOSResample&) = delete;
JOSResample& operator=(const JOSResample&) = delete;
ResampleQuality m_quality;
public:
/**
* Creates a new sound.
* \param sound The input sound.
* \param specs The target specifications.
*/
JOSResample(std::shared_ptr<ISound> sound, DeviceSpecs specs);
JOSResample(std::shared_ptr<ISound> sound, DeviceSpecs specs, ResampleQuality quality = ResampleQuality::HIGH);
virtual std::shared_ptr<IReader> createReader();
};

View File

@ -36,39 +36,39 @@ private:
typedef void (JOSResampleReader::*resample_f)(double target_factor, int length, sample_t* buffer);
/**
* The half filter length for Quality::HIGH setting.
* The half filter length for HIGH quality setting.
*/
static const int m_len_high;
/**
* The half filter length for Quality::MEDIUM setting.
* The half filter length for MEDIUM quality setting.
*/
static const int m_len_medium;
/**
* The half filter length for Quality::LOW setting.
* The half filter length for LOW quality setting.
*/
static const int m_len_low;
/**
* The filter sample step size for Quality::HIGH setting.
* The filter sample step size for HIGH quality setting.
*/
static const int m_L_high;
/**
* The filter sample step size for Quality::MEDIUM setting.
* The filter sample step size for MEDIUM quality setting.
*/
static const int m_L_medium;
/**
* The filter sample step size for Quality::LOW setting.
* The filter sample step size for LOW quality setting.
*/
static const int m_L_low;
/**
* The filter coefficients for Quality::HIGH setting.
* The filter coefficients for HIGH quality setting.
*/
static const float m_coeff_high[];
/**
* The filter coefficients for Quality::MEDIUM setting.
* The filter coefficients for MEDIUM quality setting.
*/
static const float m_coeff_medium[];
/**
* The filter coefficients for Quality::LOW setting.
* The filter coefficients for LOW quality setting.
*/
static const float m_coeff_low[];
@ -152,19 +152,13 @@ private:
void AUD_LOCAL resample(double target_factor, int length, sample_t* buffer);
public:
enum class Quality
{
LOW = 0,
MEDIUM,
HIGH,
};
/**
* Creates a resampling reader.
* \param reader The reader to mix.
* \param rate The target sampling rate.
*/
JOSResampleReader(std::shared_ptr<IReader> reader, SampleRate rate, Quality = Quality::MEDIUM);
JOSResampleReader(std::shared_ptr<IReader> reader, SampleRate rate, ResampleQuality quality = ResampleQuality::HIGH);
virtual void seek(int position);
virtual int getLength() const;

View File

@ -83,6 +83,15 @@ enum Channel
CHANNEL_MAX
};
/// Resampling algorithm and quality.
enum class ResampleQuality
{
FASTEST = 0, /// Linear resample, very fast but lowest quality.
LOW, /// JOS resample at low quality preset.
MEDIUM, /// JOS resample at medium quality preset.
HIGH /// JOS resample at high quality preset.
};
/**
* The sample rate tells how many samples are played back within one second.
* Some exotic formats may use other sample rates than provided here.

View File

@ -160,10 +160,11 @@ public:
void remove(std::shared_ptr<SequenceEntry> entry);
/**
* Creates a new reader with high quality resampling.
* Creates a new reader with indicated resampling quality.
* \param quality The resampling quality.
* \return The new reader.
*/
std::shared_ptr<IReader> createQualityReader();
std::shared_ptr<IReader> createQualityReader(ResampleQuality quality);
virtual std::shared_ptr<IReader> createReader();
};

View File

@ -74,9 +74,9 @@ public:
/**
* Creates a resampling reader.
* \param sequence The sequence data.
* \param quality Whether a high quality resample should be used for resampling.
* \param quality Resampling quality vs performance option.
*/
SequenceReader(std::shared_ptr<SequenceData> sequence, bool quality = false);
SequenceReader(std::shared_ptr<SequenceData> sequence, ResampleQuality quality = ResampleQuality::FASTEST);
/**
* Destroys the reader.

View File

@ -210,7 +210,7 @@ public:
m_buffersize = buffersize;
}
virtual void setName(std::string name)
virtual void setName(const std::string &name)
{
}
};

View File

@ -35,7 +35,7 @@ void FFMPEG::registerPlugin()
FileManager::registerOutput(plugin);
}
std::shared_ptr<IReader> FFMPEG::createReader(std::string filename, int stream)
std::shared_ptr<IReader> FFMPEG::createReader(const std::string &filename, int stream)
{
return std::shared_ptr<IReader>(new FFMPEGReader(filename, stream));
}
@ -45,7 +45,7 @@ std::shared_ptr<IReader> FFMPEG::createReader(std::shared_ptr<Buffer> buffer, in
return std::shared_ptr<IReader>(new FFMPEGReader(buffer, stream));
}
std::vector<StreamInfo> FFMPEG::queryStreams(std::string filename)
std::vector<StreamInfo> FFMPEG::queryStreams(const std::string &filename)
{
return FFMPEGReader(filename).queryStreams();
}
@ -55,7 +55,7 @@ std::vector<StreamInfo> FFMPEG::queryStreams(std::shared_ptr<Buffer> buffer)
return FFMPEGReader(buffer).queryStreams();
}
std::shared_ptr<IWriter> FFMPEG::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
std::shared_ptr<IWriter> FFMPEG::createWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
{
return std::shared_ptr<IWriter>(new FFMPEGWriter(filename, specs, format, codec, bitrate));
}

View File

@ -52,11 +52,11 @@ public:
*/
static void registerPlugin();
virtual std::shared_ptr<IReader> createReader(std::string filename, int stream = 0);
virtual std::shared_ptr<IReader> createReader(const std::string &filename, int stream = 0);
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer, int stream = 0);
virtual std::vector<StreamInfo> queryStreams(std::string filename);
virtual std::vector<StreamInfo> queryStreams(const std::string &filename);
virtual std::vector<StreamInfo> queryStreams(std::shared_ptr<Buffer> buffer);
virtual std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
virtual std::shared_ptr<IWriter> createWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
};
AUD_NAMESPACE_END

View File

@ -239,7 +239,7 @@ void FFMPEGReader::init(int stream)
m_specs.rate = (SampleRate) m_codecCtx->sample_rate;
}
FFMPEGReader::FFMPEGReader(std::string filename, int stream) :
FFMPEGReader::FFMPEGReader(const std::string &filename, int stream) :
m_pkgbuf(),
m_formatCtx(nullptr),
m_codecCtx(nullptr),

View File

@ -154,7 +154,7 @@ public:
* \exception Exception Thrown if the file specified does not exist or
* cannot be read with ffmpeg.
*/
FFMPEGReader(std::string filename, int stream = 0);
FFMPEGReader(const std::string &filename, int stream = 0);
/**
* Creates a new reader.

View File

@ -158,7 +158,7 @@ void FFMPEGWriter::close()
#endif
}
FFMPEGWriter::FFMPEGWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate) :
FFMPEGWriter::FFMPEGWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate) :
m_position(0),
m_specs(specs),
m_formatCtx(nullptr),

View File

@ -135,7 +135,7 @@ public:
* \exception Exception Thrown if the file specified does not exist or
* cannot be read with ffmpeg.
*/
FFMPEGWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
FFMPEGWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
/**
* Destroys the writer and closes the file.

View File

@ -162,7 +162,7 @@ void JackDevice::jack_shutdown(void* data)
device->m_valid = false;
}
JackDevice::JackDevice(std::string name, DeviceSpecs specs, int buffersize) :
JackDevice::JackDevice(const std::string &name, DeviceSpecs specs, int buffersize) :
m_synchronizer(this)
{
if(specs.channels == CHANNELS_INVALID)
@ -358,7 +358,7 @@ public:
m_buffersize = buffersize;
}
virtual void setName(std::string name)
virtual void setName(const std::string &name)
{
m_name = name;
}

View File

@ -151,7 +151,7 @@ public:
* \param buffersize The size of the internal buffer.
* \exception Exception Thrown if the audio device cannot be opened.
*/
JackDevice(std::string name, DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
JackDevice(const std::string &name, DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
/**
* Closes the JACK client.

View File

@ -32,7 +32,7 @@ void SndFile::registerPlugin()
FileManager::registerOutput(plugin);
}
std::shared_ptr<IReader> SndFile::createReader(std::string filename, int stream)
std::shared_ptr<IReader> SndFile::createReader(const std::string &filename, int stream)
{
return std::shared_ptr<IReader>(new SndFileReader(filename));
}
@ -42,7 +42,7 @@ std::shared_ptr<IReader> SndFile::createReader(std::shared_ptr<Buffer> buffer, i
return std::shared_ptr<IReader>(new SndFileReader(buffer));
}
std::vector<StreamInfo> SndFile::queryStreams(std::string filename)
std::vector<StreamInfo> SndFile::queryStreams(const std::string &filename)
{
return SndFileReader(filename).queryStreams();
}
@ -52,7 +52,7 @@ std::vector<StreamInfo> SndFile::queryStreams(std::shared_ptr<Buffer> buffer)
return SndFileReader(buffer).queryStreams();
}
std::shared_ptr<IWriter> SndFile::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
std::shared_ptr<IWriter> SndFile::createWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
{
return std::shared_ptr<IWriter>(new SndFileWriter(filename, specs, format, codec, bitrate));
}

View File

@ -52,11 +52,11 @@ public:
*/
static void registerPlugin();
virtual std::shared_ptr<IReader> createReader(std::string filename, int stream = 0);
virtual std::shared_ptr<IReader> createReader(const std::string &filename, int stream = 0);
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer, int stream = 0);
virtual std::vector<StreamInfo> queryStreams(std::string filename);
virtual std::vector<StreamInfo> queryStreams(const std::string &filename);
virtual std::vector<StreamInfo> queryStreams(std::shared_ptr<Buffer> buffer);
virtual std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
virtual std::shared_ptr<IWriter> createWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
};
AUD_NAMESPACE_END

View File

@ -71,7 +71,7 @@ sf_count_t SndFileReader::vio_tell(void* user_data)
return reader->m_memoffset;
}
SndFileReader::SndFileReader(std::string filename) :
SndFileReader::SndFileReader(const std::string &filename) :
m_position(0)
{
SF_INFO sfinfo;

View File

@ -103,7 +103,7 @@ public:
* \exception Exception Thrown if the file specified does not exist or
* cannot be read with libsndfile.
*/
SndFileReader(std::string filename);
SndFileReader(const std::string &filename);
/**
* Creates a new reader.

View File

@ -21,7 +21,7 @@
AUD_NAMESPACE_BEGIN
SndFileWriter::SndFileWriter(std::string filename, DeviceSpecs specs,
SndFileWriter::SndFileWriter(const std::string &filename, DeviceSpecs specs,
Container format, Codec codec, unsigned int bitrate) :
m_position(0), m_specs(specs)
{

View File

@ -69,7 +69,7 @@ public:
* \exception Exception Thrown if the file specified cannot be written
* with libsndfile.
*/
SndFileWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
SndFileWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
/**
* Destroys the writer and closes the file.

View File

@ -1131,7 +1131,7 @@ void OpenALDevice::updateStreams()
/**************************** IDevice Code ************************************/
/******************************************************************************/
OpenALDevice::OpenALDevice(DeviceSpecs specs, int buffersize, std::string name) :
OpenALDevice::OpenALDevice(DeviceSpecs specs, int buffersize, const std::string &name) :
m_name(name), m_playing(false), m_buffersize(buffersize)
{
// cannot determine how many channels or which format OpenAL uses, but
@ -1561,7 +1561,7 @@ private:
std::string m_name;
public:
OpenALDeviceFactory(std::string name = "") :
OpenALDeviceFactory(const std::string &name = "") :
m_buffersize(AUD_DEFAULT_BUFFER_SIZE),
m_name(name)
{
@ -1590,7 +1590,7 @@ public:
m_buffersize = buffersize;
}
virtual void setName(std::string name)
virtual void setName(const std::string &name)
{
}
};
@ -1599,7 +1599,7 @@ void OpenALDevice::registerPlugin()
{
auto names = OpenALDevice::getDeviceNames();
DeviceManager::registerDevice("OpenAL", std::shared_ptr<IDeviceFactory>(new OpenALDeviceFactory));
for(std::string &name : names)
for(const std::string &name : names)
{
DeviceManager::registerDevice("OpenAL - " + name, std::shared_ptr<IDeviceFactory>(new OpenALDeviceFactory(name)));
}

View File

@ -269,7 +269,7 @@ public:
* \note The buffersize will be multiplicated by three for this device.
* \exception DeviceException Thrown if the audio device cannot be opened.
*/
OpenALDevice(DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE, std::string name = "");
OpenALDevice(DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE, const std::string &name = "");
virtual ~OpenALDevice();

View File

@ -121,7 +121,7 @@ void PulseAudioDevice::playing(bool playing)
AUD_pa_threaded_mainloop_unlock(m_mainloop);
}
PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize) :
PulseAudioDevice::PulseAudioDevice(const std::string &name, DeviceSpecs specs, int buffersize) :
m_synchronizer(this),
m_playback(false),
m_state(PA_CONTEXT_UNCONNECTED),
@ -321,7 +321,7 @@ public:
m_buffersize = buffersize;
}
virtual void setName(std::string name)
virtual void setName(const std::string &name)
{
m_name = name;
}

View File

@ -128,7 +128,7 @@ public:
* \note The specification really used for opening the device may differ.
* \exception Exception Thrown if the audio device cannot be opened.
*/
PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
PulseAudioDevice(const std::string &name, DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE);
/**
* Closes the PulseAudio audio device.

View File

@ -157,7 +157,7 @@ public:
m_buffersize = buffersize;
}
virtual void setName(std::string name)
virtual void setName(const std::string &name)
{
}
};

View File

@ -458,7 +458,7 @@ public:
m_buffersize = buffersize;
}
virtual void setName(std::string name)
virtual void setName(const std::string &name)
{
}
};

View File

@ -25,7 +25,7 @@ Exception::Exception(const Exception& exception) :
{
}
Exception::Exception(std::string message, std::string file, int line) :
Exception::Exception(const std::string &message, const std::string &file, int line) :
m_message(message),
m_file(file),
m_line(line)
@ -65,7 +65,7 @@ int Exception::getLine() const
return m_line;
}
FileException::FileException(std::string message, std::string file, int line) :
FileException::FileException(const std::string &message, const std::string &file, int line) :
Exception(message, file, line)
{
}
@ -79,7 +79,7 @@ FileException::~FileException() AUD_NOEXCEPT
{
}
DeviceException::DeviceException(std::string message, std::string file, int line) :
DeviceException::DeviceException(const std::string &message, const std::string &file, int line) :
Exception(message, file, line)
{
}
@ -93,7 +93,7 @@ DeviceException::~DeviceException() AUD_NOEXCEPT
{
}
StateException::StateException(std::string message, std::string file, int line) :
StateException::StateException(const std::string &message, const std::string &file, int line) :
Exception(message, file, line)
{
}

View File

@ -28,12 +28,12 @@ AUD_NAMESPACE_BEGIN
std::unordered_map<std::string, std::shared_ptr<IDeviceFactory>> DeviceManager::m_factories;
std::shared_ptr<IDevice> DeviceManager::m_device;
void DeviceManager::registerDevice(std::string name, std::shared_ptr<IDeviceFactory> factory)
void DeviceManager::registerDevice(const std::string &name, std::shared_ptr<IDeviceFactory> factory)
{
m_factories[name] = factory;
}
std::shared_ptr<IDeviceFactory> DeviceManager::getDeviceFactory(std::string name)
std::shared_ptr<IDeviceFactory> DeviceManager::getDeviceFactory(const std::string &name)
{
auto it = m_factories.find(name);
@ -66,7 +66,7 @@ void DeviceManager::setDevice(std::shared_ptr<IDevice> device)
m_device = device;
}
void DeviceManager::openDevice(std::string name)
void DeviceManager::openDevice(const std::string &name)
{
setDevice(getDeviceFactory(name)->openDevice());
}

View File

@ -180,7 +180,7 @@ public:
{
}
virtual void setName(std::string name)
virtual void setName(const std::string &name)
{
}
};

View File

@ -718,7 +718,7 @@ void SoftwareDevice::create()
m_doppler_factor = 1.0f;
m_distance_model = DISTANCE_MODEL_INVERSE_CLAMPED;
m_flags = 0;
m_quality = false;
m_quality = ResampleQuality::FASTEST;
}
void SoftwareDevice::destroy()
@ -829,7 +829,7 @@ void SoftwareDevice::setPanning(IHandle* handle, float pan)
h->m_user_pan = pan;
}
void SoftwareDevice::setQuality(bool quality)
void SoftwareDevice::setQuality(ResampleQuality quality)
{
m_quality = quality;
}
@ -886,10 +886,14 @@ std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<IReader> reader, b
std::shared_ptr<ResampleReader> resampler;
// resample
if(m_quality)
resampler = std::shared_ptr<ResampleReader>(new JOSResampleReader(reader, m_specs.rate));
else
if (m_quality == ResampleQuality::FASTEST)
{
resampler = std::shared_ptr<ResampleReader>(new LinearResampleReader(reader, m_specs.rate));
}
else
{
resampler = std::shared_ptr<ResampleReader>(new JOSResampleReader(reader, m_specs.rate, m_quality));
}
reader = std::shared_ptr<IReader>(resampler);
// rechannel

View File

@ -23,7 +23,7 @@
AUD_NAMESPACE_BEGIN
File::File(std::string filename, int stream) :
File::File(const std::string &filename, int stream) :
m_filename(filename), m_stream(stream)
{
}

View File

@ -43,7 +43,7 @@ void FileManager::registerOutput(std::shared_ptr<aud::IFileOutput> output)
outputs().push_back(output);
}
std::shared_ptr<IReader> FileManager::createReader(std::string filename, int stream)
std::shared_ptr<IReader> FileManager::createReader(const std::string &filename, int stream)
{
for(std::shared_ptr<IFileInput> input : inputs())
{
@ -71,7 +71,7 @@ std::shared_ptr<IReader> FileManager::createReader(std::shared_ptr<Buffer> buffe
AUD_THROW(FileException, "The file couldn't be read with any installed file reader.");
}
std::vector<StreamInfo> FileManager::queryStreams(std::string filename)
std::vector<StreamInfo> FileManager::queryStreams(const std::string &filename)
{
for(std::shared_ptr<IFileInput> input : inputs())
{
@ -99,7 +99,7 @@ std::vector<StreamInfo> FileManager::queryStreams(std::shared_ptr<Buffer> buffer
AUD_THROW(FileException, "The file couldn't be read with any installed file reader.");
}
std::shared_ptr<IWriter> FileManager::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
std::shared_ptr<IWriter> FileManager::createWriter(const std::string &filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
{
for(std::shared_ptr<IFileOutput> output : outputs())
{

View File

@ -22,7 +22,7 @@
AUD_NAMESPACE_BEGIN
std::shared_ptr<IWriter> FileWriter::createWriter(std::string filename,DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
std::shared_ptr<IWriter> FileWriter::createWriter(const std::string &filename,DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
{
return FileManager::createWriter(filename, specs, format, codec, bitrate);
}

View File

@ -19,15 +19,14 @@
AUD_NAMESPACE_BEGIN
JOSResample::JOSResample(std::shared_ptr<ISound> sound,
DeviceSpecs specs) :
SpecsChanger(sound, specs)
JOSResample::JOSResample(std::shared_ptr<ISound> sound, DeviceSpecs specs, ResampleQuality quality) :
SpecsChanger(sound, specs), m_quality(quality)
{
}
std::shared_ptr<IReader> JOSResample::createReader()
{
return std::shared_ptr<IReader>(new JOSResampleReader(getReader(), m_specs.rate));
return std::shared_ptr<IReader>(new JOSResampleReader(getReader(), m_specs.rate, m_quality));
}
AUD_NAMESPACE_END

View File

@ -45,7 +45,7 @@ static inline int lrint_impl(double x)
AUD_NAMESPACE_BEGIN
JOSResampleReader::JOSResampleReader(std::shared_ptr<IReader> reader, SampleRate rate, Quality quality) :
JOSResampleReader::JOSResampleReader(std::shared_ptr<IReader> reader, SampleRate rate, ResampleQuality quality) :
ResampleReader(reader, rate),
m_channels(CHANNELS_INVALID),
m_n(0),
@ -55,17 +55,17 @@ JOSResampleReader::JOSResampleReader(std::shared_ptr<IReader> reader, SampleRate
{
switch(quality)
{
case Quality::LOW:
case ResampleQuality::LOW:
m_len = m_len_low;
m_L = m_L_low;
m_coeff = m_coeff_low;
break;
case Quality::MEDIUM:
case ResampleQuality::MEDIUM:
m_len = m_len_medium;
m_L = m_L_medium;
m_coeff = m_coeff_medium;
break;
case Quality::HIGH:
case ResampleQuality::HIGH:
m_len = m_len_high;
m_L = m_L_high;
m_coeff = m_coeff_high;

View File

@ -100,9 +100,9 @@ void Sequence::remove(std::shared_ptr<SequenceEntry> entry)
m_sequence->remove(entry);
}
std::shared_ptr<IReader> Sequence::createQualityReader()
std::shared_ptr<IReader> Sequence::createQualityReader(ResampleQuality quality)
{
return std::shared_ptr<IReader>(new SequenceReader(m_sequence, true));
return std::shared_ptr<IReader>(new SequenceReader(m_sequence, quality));
}
std::shared_ptr<IReader> Sequence::createReader()

View File

@ -25,7 +25,7 @@
AUD_NAMESPACE_BEGIN
SequenceReader::SequenceReader(std::shared_ptr<SequenceData> sequence, bool quality) :
SequenceReader::SequenceReader(std::shared_ptr<SequenceData> sequence, ResampleQuality quality) :
m_position(0), m_device(sequence->m_specs), m_sequence(sequence), m_status(0), m_entry_status(0)
{
m_device.setQuality(quality);

View File

@ -1655,7 +1655,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
col.label(text=rpt_("and Windows driver version %s or newer") % driver_version,
icon='BLANK1', translate=False)
elif sys.platform.startswith("linux"):
driver_version = "XX.XX.26918.50"
driver_version = "XX.XX.27642.38"
col.label(
text=rpt_("Requires Intel GPU with Xe-HPG architecture and"),
icon='BLANK1',

View File

@ -866,7 +866,7 @@ static const int lowest_supported_driver_version_win = 1015186;
* This information is returned by `ocloc query OCL_DRIVER_VERSION`.*/
static const int lowest_supported_driver_version_neo = 28044;
# else
static const int lowest_supported_driver_version_neo = 26918;
static const int lowest_supported_driver_version_neo = 27642;
# endif
int parse_driver_build_version(const sycl::device &device)

View File

@ -882,7 +882,7 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
# Set defaults for spir64 and spir64_gen options
if(NOT DEFINED CYCLES_ONEAPI_SYCL_OPTIONS_spir64)
set(CYCLES_ONEAPI_SYCL_OPTIONS_spir64 "-options '-ze-opt-regular-grf-kernel integrator_intersect -ze-opt-large-grf-kernel shade -ze-opt-no-local-to-generic'")
set(CYCLES_ONEAPI_SYCL_OPTIONS_spir64 "-options '-ze-opt-large-register-file -ze-opt-regular-grf-kernel integrator_intersect -ze-opt-large-grf-kernel shade -ze-opt-no-local-to-generic'")
endif()
if(NOT DEFINED CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen)
set(CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "${CYCLES_ONEAPI_SYCL_OPTIONS_spir64}" CACHE STRING "Extra build options for spir64_gen target")

View File

@ -19,7 +19,7 @@ PERFORMANCE OF THIS SOFTWARE.
------
** Audaspace; version 1.3.0 -- https://audaspace.github.io/
** Audaspace; version 1.4+ (ae29ce2) -- https://audaspace.github.io/
** Cuda Wrangler; version cbf465b -- https://github.com/CudaWrangler/cuew
** Draco; version 1.3.6 -- https://google.github.io/draco/
** Embree; version 4.1.0 -- https://github.com/embree/embree
@ -244,7 +244,7 @@ See the License for the specific language governing permissions and
limitations under the License.
* For Audaspace see also this required NOTICE:
Copyright © 2009-2020 Jörg Müller. All rights reserved.
Copyright © 2009-2023 Jörg Müller. All rights reserved.
* For Cuda Wrangler see also this required NOTICE:
Copyright 2011-2014 Blender Foundation
* For Draco see also this required NOTICE:

View File

@ -4586,6 +4586,9 @@ def km_grease_pencil_paint_mode(_params):
*_template_items_hide_reveal_actions("grease_pencil.layer_hide", "grease_pencil.layer_reveal"),
("paint.sample_color", {"type": 'X', "value": 'PRESS', "shift": True}, None),
# Isolate Layer
("grease_pencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
])
return keymap
@ -4659,6 +4662,9 @@ def km_grease_pencil_edit_mode(params):
"ctrl": True, "repeat": True}, {"properties": [("direction", "DOWN")]}),
("grease_pencil.reorder", {"type": 'DOWN_ARROW', "value": 'PRESS',
"ctrl": True, "shift": True}, {"properties": [("direction", "BOTTOM")]}),
# Isolate Layer
("grease_pencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
])
return keymap

View File

@ -658,70 +658,77 @@ class WM_MT_operator_presets(Menu):
class WM_OT_operator_presets_cleanup(Operator):
"""Remove outdated operator properties from presets that may cause problems"""
bl_idname = "wm.operator_presets_cleanup"
bl_label = "Clean Up Operator Presets"
bl_description = "Remove outdated operator properties from presets that may cause problems"
operator: StringProperty(name="operator")
properties: CollectionProperty(name="properties", type=OperatorFileListElement)
def cleanup_preset(self, filepath, properties):
from pathlib import Path
file = Path(filepath)
if not (file.is_file() and filepath.suffix == ".py"):
def _cleanup_preset(self, filepath, properties_exclude):
import os
import re
if not (os.path.isfile(filepath) and os.path.splitext(filepath)[1].lower() == ".py"):
return
lines = file.read_text().splitlines(True)
if len(lines) == 0:
with open(filepath, "r", encoding="utf-8") as fh:
lines = fh.read().splitlines(True)
if not lines:
return
new_lines = []
for line in lines:
if not any(line.startswith(("op.%s" % prop)) for prop in properties):
new_lines.append(line)
file.write_text("".join(new_lines))
regex_exclude = re.compile("(" + "|".join([re.escape("op." + prop) for prop in properties_exclude]) + ")\\b")
lines = [line for line in lines if not regex_exclude.match(line)]
with open(filepath, "w", encoding="utf-8") as fh:
fh.write("".join(lines))
def cleanup_operators_presets(self, operators, properties):
base_preset_directory = bpy.utils.user_resource(
'SCRIPTS', path="presets", create=False)
def _cleanup_operators_presets(self, operators, properties_exclude):
import os
base_preset_directory = bpy.utils.user_resource('SCRIPTS', path="presets", create=False)
if not base_preset_directory:
return
for operator in operators:
from pathlib import Path
operator_path = AddPresetOperator.operator_path(operator)
directory = Path(base_preset_directory, operator_path)
directory = os.path.join(base_preset_directory, operator_path)
if not directory.is_dir():
if not os.path.isdir(directory):
continue
for filepath in directory.iterdir():
self.cleanup_preset(filepath, properties)
for filename in os.listdir(directory):
self._cleanup_preset(os.path.join(directory, filename), properties_exclude)
def execute(self, context):
properties = []
properties_exclude = []
operators = []
if self.operator:
operators.append(self.operator)
for prop in self.properties:
properties.append(prop.name)
properties_exclude.append(prop.name)
else:
# Cleanup by default I/O Operators Presets
operators = ['WM_OT_alembic_export',
'WM_OT_alembic_import',
'WM_OT_collada_export',
'WM_OT_collada_import',
'WM_OT_gpencil_export_svg',
'WM_OT_gpencil_export_pdf',
'WM_OT_gpencil_export_svg',
'WM_OT_gpencil_import_svg',
'WM_OT_obj_export',
'WM_OT_obj_import',
'WM_OT_ply_export',
'WM_OT_ply_import',
'WM_OT_stl_export',
'WM_OT_stl_import',
'WM_OT_usd_export',
'WM_OT_usd_import',
]
properties = ["filepath", "directory", "files", "filename"]
operators = [
"WM_OT_alembic_export",
"WM_OT_alembic_import",
"WM_OT_collada_export",
"WM_OT_collada_import",
"WM_OT_gpencil_export_svg",
"WM_OT_gpencil_export_pdf",
"WM_OT_gpencil_export_svg",
"WM_OT_gpencil_import_svg",
"WM_OT_obj_export",
"WM_OT_obj_import",
"WM_OT_ply_export",
"WM_OT_ply_import",
"WM_OT_stl_export",
"WM_OT_stl_import",
"WM_OT_usd_export",
"WM_OT_usd_import",
]
properties_exclude = [
"filepath",
"directory",
"files",
"filename"
]
self.cleanup_operators_presets(operators, properties)
self._cleanup_operators_presets(operators, properties_exclude)
return {'FINISHED'}

View File

@ -410,7 +410,11 @@ class NODE_MT_geometry_node_GEO_MESH_OPERATIONS(Menu):
node_add_menu.add_node_type(layout, "GeometryNodeFlipFaces")
node_add_menu.add_node_type(layout, "GeometryNodeMeshBoolean")
node_add_menu.add_node_type(layout, "GeometryNodeMeshToCurve")
if context.preferences.experimental.use_new_volume_nodes:
node_add_menu.add_node_type(layout, "GeometryNodeMeshToDensityGrid")
node_add_menu.add_node_type(layout, "GeometryNodeMeshToPoints")
if context.preferences.experimental.use_new_volume_nodes:
node_add_menu.add_node_type(layout, "GeometryNodeMeshToSDFGrid")
node_add_menu.add_node_type(layout, "GeometryNodeMeshToVolume")
node_add_menu.add_node_type(layout, "GeometryNodeScaleElements")
node_add_menu.add_node_type(layout, "GeometryNodeSplitEdges")
@ -471,11 +475,14 @@ class NODE_MT_category_GEO_POINT(Menu):
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeDistributePointsInVolume")
if context.preferences.experimental.use_new_volume_nodes:
node_add_menu.add_node_type(layout, "GeometryNodeDistributePointsInGrid")
node_add_menu.add_node_type(layout, "GeometryNodeDistributePointsOnFaces")
layout.separator()
node_add_menu.add_node_type(layout, "GeometryNodePoints")
node_add_menu.add_node_type(layout, "GeometryNodePointsToCurves")
if context.preferences.experimental.use_new_volume_nodes:
node_add_menu.add_node_type(layout, "GeometryNodePointsToSDFGrid")
node_add_menu.add_node_type(layout, "GeometryNodePointsToVertices")
node_add_menu.add_node_type(layout, "GeometryNodePointsToVolume")
layout.separator()
@ -711,6 +718,8 @@ class NODE_MT_geometry_node_GEO_VOLUME_OPERATIONS(Menu):
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeVolumeToMesh")
if context.preferences.experimental.use_new_volume_nodes:
node_add_menu.add_node_type(layout, "GeometryNodeGridToMesh")
node_add_menu.draw_assets_for_catalog(layout, "Volume/Operations")

View File

@ -1433,6 +1433,65 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True)
def brush_basic_grease_pencil_paint_settings(layout, context, brush, *, compact=False):
tool_settings = context.tool_settings
settings = tool_settings.gpencil_paint
gp_settings = brush.gpencil_settings
tool = context.workspace.tools.from_space_view3d_mode(context.mode, create=False)
if gp_settings is None:
return
grease_pencil_tool = brush.gpencil_tool
UnifiedPaintPanel.prop_unified(
layout,
context,
brush,
"size",
unified_name="use_unified_size",
pressure_name="use_pressure_size",
text="Radius",
slider=True,
header=compact,
)
if brush.use_pressure_size and not compact:
col = layout.column()
col.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True,
use_negative_slope=True)
UnifiedPaintPanel.prop_unified(
layout,
context,
brush,
"strength",
unified_name="use_unified_strength",
pressure_name="use_pressure_strength",
slider=True,
header=compact,
)
if brush.use_pressure_strength and not compact:
col = layout.column()
col.template_curve_mapping(gp_settings, "curve_strength", brush=True,
use_negative_slope=True)
if grease_pencil_tool == 'DRAW':
layout.prop(gp_settings, "active_smooth_factor")
row = layout.row(align=True)
if compact:
row.prop(gp_settings, "caps_type", text="", expand=True)
else:
row.prop(gp_settings, "caps_type", text="Caps Type")
# TODO: Remove this when caps work.
row.enabled = False
elif grease_pencil_tool == 'ERASE':
layout.prop(gp_settings, "eraser_mode", expand=True)
if gp_settings.eraser_mode == "HARD":
layout.prop(gp_settings, "use_keep_caps_eraser")
layout.prop(gp_settings, "use_active_layer_only")
def brush_basic_gpencil_sculpt_settings(layout, _context, brush, *, compact=False):
if brush is None:
return

View File

@ -1699,7 +1699,7 @@ class _defs_weight_paint:
ob.data.use_paint_mask_vertex)):
return VIEW3D_PT_tools_active._tools_select
elif context.pose_object:
return (_defs_view3d_select.select,)
return VIEW3D_PT_tools_active._tools_select
return ()
@staticmethod

View File

@ -605,36 +605,12 @@ class _draw_tool_settings_context_mode:
)
brush_basic__draw_color_selector(context, layout, brush, brush.gpencil_settings, None)
UnifiedPaintPanel.prop_unified(
layout,
context,
brush,
"size",
unified_name="use_unified_size",
pressure_name="use_pressure_size",
text="Radius",
slider=True,
header=True,
from bl_ui.properties_paint_common import (
brush_basic__draw_color_selector,
brush_basic_grease_pencil_paint_settings,
)
UnifiedPaintPanel.prop_unified(
layout,
context,
brush,
"strength",
pressure_name="use_pressure_strength",
unified_name="use_unified_strength",
slider=True,
header=True,
)
if grease_pencil_tool == 'DRAW':
layout.prop(brush.gpencil_settings, "active_smooth_factor")
elif grease_pencil_tool == 'ERASE':
layout.prop(brush.gpencil_settings, "eraser_mode", expand=True)
if brush.gpencil_settings.eraser_mode == "HARD":
layout.prop(brush.gpencil_settings, "use_keep_caps_eraser")
layout.prop(brush.gpencil_settings, "use_active_layer_only")
brush_basic_grease_pencil_paint_settings(layout, context, brush, compact=True)
return True

View File

@ -2489,6 +2489,190 @@ class VIEW3D_PT_gpencil_brush_presets(Panel, PresetPanel):
preset_add_operator = "scene.gpencil_brush_preset_add"
class GreasePencilV3PaintPanel:
bl_context = ".grease_pencil_paint"
bl_category = "Tool"
@classmethod
def poll(cls, context):
if context.space_data.type in {'VIEW_3D', 'PROPERTIES'}:
# Hide for tools not using brushes.
if tool_use_brush(context) is False:
return False
return True
else:
return True
class VIEW3D_PT_tools_grease_pencil_v3_brush_select(Panel, View3DPanel, GreasePencilV3PaintPanel):
bl_label = "Brushes"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
tool_settings = context.scene.tool_settings
gpencil_paint = tool_settings.gpencil_paint
row = layout.row()
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
col = row.column()
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
brush = tool_settings.gpencil_paint.brush
if brush is not None:
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
if brush.use_custom_icon:
layout.row().prop(brush, "icon_filepath", text="")
class VIEW3D_PT_tools_grease_pencil_v3_brush_settings(Panel, View3DPanel, GreasePencilV3PaintPanel):
bl_label = "Brush Settings"
bl_options = {'DEFAULT_CLOSED'}
def draw_header_preset(self, _context):
VIEW3D_PT_gpencil_brush_presets.draw_panel_header(self.layout)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
tool_settings = context.scene.tool_settings
gpencil_paint = tool_settings.gpencil_paint
brush = gpencil_paint.brush
if brush is not None:
gp_settings = brush.gpencil_settings
if brush.gpencil_tool in {'DRAW', 'FILL'}:
row = layout.row(align=True)
row_mat = row.row()
if gp_settings.use_material_pin:
row_mat.template_ID(gp_settings, "material", live_icon=True)
else:
row_mat.template_ID(context.active_object, "active_material", live_icon=True)
row_mat.enabled = False # will otherwise allow changing material in active slot
row.prop(gp_settings, "use_material_pin", text="")
if not self.is_popover:
from bl_ui.properties_paint_common import (
brush_basic_grease_pencil_paint_settings,
)
brush_basic_grease_pencil_paint_settings(layout, context, brush, compact=False)
class VIEW3D_PT_tools_grease_pencil_v3_brush_mixcolor(View3DPanel, Panel):
bl_context = ".grease_pencil_paint"
bl_label = "Color"
bl_category = "Tool"
@classmethod
def poll(cls, context):
ob = context.object
tool_settings = context.tool_settings
settings = tool_settings.gpencil_paint
brush = settings.brush
if ob is None or brush is None:
return False
if context.region.type == 'TOOL_HEADER':
return False
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
tool = ToolSelectPanelHelper.tool_active_from_context(context)
if tool and tool.idname in {'builtin.cutter', 'builtin.eyedropper', 'builtin.interpolate'}:
return False
if brush.gpencil_tool == 'TINT':
return True
if brush.gpencil_tool not in {'DRAW', 'FILL'}:
return False
return True
def draw(self, context):
layout = self.layout
tool_settings = context.tool_settings
settings = tool_settings.gpencil_paint
brush = settings.brush
gp_settings = brush.gpencil_settings
row = layout.row()
row.prop(settings, "color_mode", expand=True)
layout.use_property_split = True
layout.use_property_decorate = False
col = layout.column()
col.enabled = settings.color_mode == 'VERTEXCOLOR'
col.template_color_picker(brush, "color", value_slider=True)
sub_row = col.row(align=True)
UnifiedPaintPanel.prop_unified_color(sub_row, context, brush, "color", text="")
UnifiedPaintPanel.prop_unified_color(sub_row, context, brush, "secondary_color", text="")
sub_row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="")
if brush.gpencil_tool in {'DRAW', 'FILL'}:
col.prop(gp_settings, "vertex_mode", text="Mode")
col.prop(gp_settings, "vertex_color_factor", slider=True, text="Mix Factor")
class VIEW3D_PT_tools_grease_pencil_v3_brush_mix_palette(View3DPanel, Panel):
bl_context = ".grease_pencil_paint"
bl_label = "Palette"
bl_category = "Tool"
bl_parent_id = "VIEW3D_PT_tools_grease_pencil_v3_brush_mixcolor"
@classmethod
def poll(cls, context):
ob = context.object
tool_settings = context.tool_settings
settings = tool_settings.gpencil_paint
brush = settings.brush
if ob is None or brush is None:
return False
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
tool = ToolSelectPanelHelper.tool_active_from_context(context)
if tool and tool.idname in {'builtin.cutter', 'builtin.eyedropper', 'builtin.interpolate'}:
return False
if brush.gpencil_tool == 'TINT':
return True
if brush.gpencil_tool not in {'DRAW', 'FILL'}:
return False
return True
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
tool_settings = context.tool_settings
settings = tool_settings.gpencil_paint
brush = settings.brush
col = layout.column()
col.enabled = settings.color_mode == 'VERTEXCOLOR'
row = col.row(align=True)
row.template_ID(settings, "palette", new="palette.new")
if settings.palette:
col.template_palette(settings, "palette", color=True)
classes = (
VIEW3D_MT_brush_context_menu,
VIEW3D_MT_brush_gpencil_context_menu,
@ -2580,6 +2764,11 @@ classes = (
VIEW3D_PT_tools_grease_pencil_brush_mixcolor,
VIEW3D_PT_tools_grease_pencil_brush_mix_palette,
VIEW3D_PT_tools_grease_pencil_v3_brush_select,
VIEW3D_PT_tools_grease_pencil_v3_brush_settings,
VIEW3D_PT_tools_grease_pencil_v3_brush_mixcolor,
VIEW3D_PT_tools_grease_pencil_v3_brush_mix_palette,
VIEW3D_PT_tools_grease_pencil_brush_paint_falloff,
VIEW3D_PT_tools_grease_pencil_brush_sculpt_falloff,
VIEW3D_PT_tools_grease_pencil_brush_weight_falloff,

View File

@ -1330,6 +1330,11 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define GEO_NODE_SORT_ELEMENTS 2123
#define GEO_NODE_MENU_SWITCH 2124
#define GEO_NODE_SAMPLE_GRID 2125
#define GEO_NODE_MESH_TO_DENSITY_GRID 2126
#define GEO_NODE_MESH_TO_SDF_GRID 2127
#define GEO_NODE_POINTS_TO_SDF_GRID 2128
#define GEO_NODE_GRID_TO_MESH 2129
#define GEO_NODE_DISTRIBUTE_POINTS_IN_GRID 2130
/** \} */

View File

@ -44,6 +44,8 @@ Mesh *volume_to_mesh(const openvdb::GridBase &grid,
float threshold,
float adaptivity);
Mesh *volume_grid_to_mesh(const openvdb::GridBase &grid, float threshold, float adaptivity);
/**
* Convert an OpenVDB volume grid to corresponding mesh data: vertex positions and quad and
* triangle indices.

View File

@ -1779,6 +1779,8 @@ void what_does_obaction(Object *ob,
/* execute effects of Action on to workob (or its PoseChannels) */
BKE_animsys_evaluate_animdata(&workob->id, &adt, anim_eval_context, ADT_RECALC_ANIM, false);
}
/* Ensure stack memory set here isn't accessed later, see !118847. */
workob->runtime = nullptr;
}
void BKE_pose_check_uids_unique_and_report(const bPose *pose)

View File

@ -1118,16 +1118,9 @@ void BKE_appdir_app_templates(ListBase *templates)
*/
static void where_is_temp(char *tempdir, const size_t tempdir_maxncpy, const char *userdir)
{
tempdir[0] = '\0';
if (userdir && userdir[0] != '\0' && BLI_is_dir(userdir)) {
BLI_strncpy(tempdir, userdir, tempdir_maxncpy);
/* Add a trailing slash if needed. */
BLI_path_slash_ensure(tempdir, tempdir_maxncpy);
if (BLI_temp_directory_path_copy_if_valid(tempdir, tempdir_maxncpy, userdir)) {
return;
}
BLI_temp_directory_path_get(tempdir, tempdir_maxncpy);
}

View File

@ -724,13 +724,17 @@ static ModifierData &legacy_object_modifier_common(Object &object,
return false;
}
StringRefNull rna_path = fcurve->rna_path;
char legacy_name_esc[MAX_NAME * 2];
BLI_str_escape(legacy_name_esc, legacy_md.name, sizeof(legacy_name_esc));
const std::string legacy_root_path = fmt::format("grease_pencil_modifiers[\"{}\"]",
legacy_md.name);
legacy_name_esc);
if (!rna_path.startswith(legacy_root_path)) {
return false;
}
char new_name_esc[MAX_NAME * 2];
BLI_str_escape(new_name_esc, new_md.name, sizeof(new_name_esc));
const std::string new_rna_path = fmt::format(
"modifiers[\"{}\"]{}", new_md.name, rna_path.substr(int64_t(legacy_root_path.size())));
"modifiers[\"{}\"]{}", new_name_esc, rna_path.substr(int64_t(legacy_root_path.size())));
MEM_freeN(fcurve->rna_path);
fcurve->rna_path = BLI_strdupn(new_rna_path.c_str(), new_rna_path.size());
return true;

View File

@ -599,7 +599,8 @@ void BKE_sound_load(Main *bmain, bSound *sound)
AUD_Device *BKE_sound_mixdown(const Scene *scene, AUD_DeviceSpecs specs, int start, float volume)
{
sound_verify_evaluated_id(&scene->id);
return AUD_openMixdownDevice(specs, scene->sound_scene, volume, start / FPS);
return AUD_openMixdownDevice(
specs, scene->sound_scene, volume, AUD_RESAMPLE_QUALITY_MEDIUM, start / FPS);
}
void BKE_sound_create_scene(Scene *scene)

View File

@ -184,6 +184,13 @@ Mesh *volume_to_mesh(const openvdb::GridBase &grid,
return mesh;
}
Mesh *volume_grid_to_mesh(const openvdb::GridBase &grid,
const float threshold,
const float adaptivity)
{
return volume_to_mesh(grid, {VOLUME_TO_MESH_RESOLUTION_MODE_GRID}, threshold, adaptivity);
}
#endif /* WITH_OPENVDB */
} // namespace blender::bke

View File

@ -0,0 +1,190 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <array>
#include "BLI_linear_allocator.hh"
#include "BLI_struct_equality_utils.hh"
#include "BLI_utility_mixins.hh"
namespace blender::linear_allocator {
/**
* The list is a linked list of segments containing multiple elements. The capacity of each segment
* is a template parameter because that removes the need to store it for every segment.
*/
template<typename T, int64_t Capacity> struct ChunkedListSegment {
/** Pointer to the next segment in the list. */
ChunkedListSegment *next = nullptr;
/**
* Number of constructed elements in this segment. The constructed elements are always at the
* beginning of the array below.
*/
int64_t size = 0;
/**
* The memory that actually contains the values in the end. The values are constructed and
* destructed by higher level code.
*/
std::array<TypedBuffer<T>, Capacity> values;
};
/**
* This is a special purpose container data structure that can be used to efficiently gather many
* elements into many (small) lists for later retrieval. Insertion order is *not* maintained.
*
* To use this data structure, one has to have a separate #LinearAllocator which is passed to the
* `append` function. This allows the same allocator to be used by many lists. Passing it into the
* append function also removes the need to store the allocator pointer in every list.
*
* It is an improvement over #Vector because it does not require any reallocations. #VectorList
* could also be used to overcome the reallocation issue.
*
* This data structure is also an improvement over #VectorList because:
* - It has a much lower memory footprint when empty.
* - Allows using a #LinearAllocator for all allocations, without storing the pointer to it in
* every vector.
* - It wastes less memory due to over-allocations.
*/
template<typename T, int64_t SegmentCapacity = 4> class ChunkedList : NonCopyable {
private:
using Segment = ChunkedListSegment<T, SegmentCapacity>;
Segment *current_segment_ = nullptr;
public:
ChunkedList() = default;
ChunkedList(ChunkedList &&other)
{
current_segment_ = other.current_segment_;
other.current_segment_ = nullptr;
}
~ChunkedList()
{
/* This code assumes that the #ChunkedListSegment does not have to be destructed if the
* contained type is trivially destructible. */
static_assert(std::is_trivially_destructible_v<ChunkedListSegment<int, 4>>);
if constexpr (!std::is_trivially_destructible_v<T>) {
for (Segment *segment = current_segment_; segment; segment = segment->next) {
for (const int64_t i : IndexRange(segment->size)) {
T &value = *segment->values[i];
std::destroy_at(&value);
}
}
}
}
ChunkedList &operator=(ChunkedList &&other)
{
if (this == &other) {
return *this;
}
std::destroy_at(this);
new (this) ChunkedList(std::move(other));
return *this;
}
/**
* Add an element to the list. The insertion order is not maintained. The given allocator is used
* to allocate any extra memory that may be needed.
*/
void append(LinearAllocator<> &allocator, const T &value)
{
this->append_as(allocator, value);
}
void append(LinearAllocator<> &allocator, T &&value)
{
this->append_as(allocator, std::move(value));
}
template<typename... Args> void append_as(LinearAllocator<> &allocator, Args &&...args)
{
if (current_segment_ == nullptr || current_segment_->size == SegmentCapacity) {
/* Allocate a new segment if necessary. */
static_assert(std::is_trivially_destructible_v<Segment>);
Segment *new_segment = allocator.construct<Segment>().release();
new_segment->next = current_segment_;
current_segment_ = new_segment;
}
T *value = &*current_segment_->values[current_segment_->size++];
new (value) T(std::forward<Args>(args)...);
}
class ConstIterator {
private:
const Segment *segment_ = nullptr;
int64_t index_ = 0;
public:
ConstIterator(const Segment *segment, int64_t index = 0) : segment_(segment), index_(index) {}
ConstIterator &operator++()
{
index_++;
if (index_ == segment_->size) {
segment_ = segment_->next;
index_ = 0;
}
return *this;
}
const T &operator*() const
{
return *segment_->values[index_];
}
BLI_STRUCT_EQUALITY_OPERATORS_2(ConstIterator, segment_, index_)
};
class MutableIterator {
private:
Segment *segment_ = nullptr;
int64_t index_ = 0;
public:
MutableIterator(Segment *segment, int64_t index = 0) : segment_(segment), index_(index) {}
MutableIterator &operator++()
{
index_++;
if (index_ == segment_->size) {
segment_ = segment_->next;
index_ = 0;
}
return *this;
}
T &operator*()
{
return *segment_->values[index_];
}
BLI_STRUCT_EQUALITY_OPERATORS_2(MutableIterator, segment_, index_)
};
ConstIterator begin() const
{
return ConstIterator(current_segment_, 0);
}
ConstIterator end() const
{
return ConstIterator(nullptr, 0);
}
MutableIterator begin()
{
return MutableIterator(current_segment_, 0);
}
MutableIterator end()
{
return MutableIterator(nullptr, 0);
}
};
} // namespace blender::linear_allocator

View File

@ -14,6 +14,14 @@
extern "C" {
#endif
/**
* Sets `temp_directory` from `dirpath` when it's a valid directory.
* Simple sanitize operations are performed and a trailing slash is ensured.
*/
bool BLI_temp_directory_path_copy_if_valid(char *temp_directory,
const size_t buffer_size,
const char *dirpath);
/* Get the path to a directory suitable for temporary files.
*
* The return path is guaranteed to exist and to be a directory, as well as to contain a trailing

View File

@ -263,6 +263,7 @@ set(SRC
BLI_lazy_threading.hh
BLI_length_parameterize.hh
BLI_linear_allocator.hh
BLI_linear_allocator_chunked_list.hh
BLI_link_utils.h
BLI_linklist.h
BLI_linklist_lockfree.h
@ -521,6 +522,7 @@ if(WITH_GTESTS)
tests/BLI_kdtree_test.cc
tests/BLI_length_parameterize_test.cc
tests/BLI_linear_allocator_test.cc
tests/BLI_linear_allocator_chunked_list_test.cc
tests/BLI_linklist_lockfree_test.cc
tests/BLI_listbase_test.cc
tests/BLI_map_test.cc

View File

@ -8,6 +8,34 @@
#include "BLI_path_util.h"
#include "BLI_string.h"
bool BLI_temp_directory_path_copy_if_valid(char *temp_directory,
const size_t buffer_size,
const char *dirpath)
{
if (dirpath == NULL) {
return false;
}
/* Disallow paths starting with two forward slashes. While they are valid paths,
* Blender interprets them as relative in situations relative paths aren't supported,
* see #95411. */
while (UNLIKELY(dirpath[0] == '/' && dirpath[1] == '/')) {
dirpath++;
}
if (dirpath[0] == '\0') {
return false;
}
if (!BLI_is_dir(dirpath)) {
return false;
}
BLI_strncpy(temp_directory, dirpath, buffer_size);
/* Add a trailing slash if needed. */
BLI_path_slash_ensure(temp_directory, buffer_size);
return true;
}
void BLI_temp_directory_path_get(char *temp_directory, const size_t buffer_size)
{
temp_directory[0] = '\0';
@ -24,9 +52,9 @@ void BLI_temp_directory_path_get(char *temp_directory, const size_t buffer_size)
};
for (int i = 0; i < ARRAY_SIZE(env_vars); i++) {
const char *tmp = BLI_getenv(env_vars[i]);
if (tmp && (tmp[0] != '\0') && BLI_is_dir(tmp)) {
BLI_strncpy(temp_directory, tmp, buffer_size);
if (BLI_temp_directory_path_copy_if_valid(
temp_directory, buffer_size, BLI_getenv(env_vars[i])))
{
break;
}
}
@ -34,10 +62,6 @@ void BLI_temp_directory_path_get(char *temp_directory, const size_t buffer_size)
if (temp_directory[0] == '\0') {
BLI_strncpy(temp_directory, "/tmp/", buffer_size);
}
else {
/* Add a trailing slash if needed. */
BLI_path_slash_ensure(temp_directory, buffer_size);
}
BLI_dir_create_recursive(temp_directory);
}

View File

@ -0,0 +1,102 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: Apache-2.0 */
#include "testing/testing.h"
#include <iostream>
#include "BLI_linear_allocator_chunked_list.hh"
#include "BLI_set.hh"
#include "BLI_strict_flags.h" /* Keep last. */
namespace blender::linear_allocator::tests {
TEST(LinearAllocator_ChunkedList, Append)
{
LinearAllocator<> allocator;
ChunkedList<std::string> list;
list.append(allocator, "1");
list.append(allocator, "2");
list.append(allocator, "this_is_an_extra_long_string");
Set<std::string> retrieved_values;
for (const std::string &value : const_cast<const ChunkedList<std::string> &>(list)) {
retrieved_values.add(value);
}
EXPECT_EQ(retrieved_values.size(), 3);
EXPECT_TRUE(retrieved_values.contains("1"));
EXPECT_TRUE(retrieved_values.contains("2"));
EXPECT_TRUE(retrieved_values.contains("this_is_an_extra_long_string"));
}
TEST(LinearAllocator_ChunkedList, AppendMany)
{
LinearAllocator<> allocator;
ChunkedList<int> list;
for (const int64_t i : IndexRange(10000)) {
list.append(allocator, int(i));
}
Set<int> values;
for (const int value : list) {
values.add(value);
}
EXPECT_EQ(values.size(), 10000);
}
TEST(LinearAllocator_ChunkedList, Move)
{
LinearAllocator<> allocator;
ChunkedList<int> a;
a.append(allocator, 1);
ChunkedList<int> b = std::move(a);
a.append(allocator, 2);
b.append(allocator, 3);
{
Set<int> a_values;
for (const int value : a) {
a_values.add(value);
}
Set<int> b_values;
for (const int value : b) {
b_values.add(value);
}
EXPECT_EQ(a_values.size(), 1);
EXPECT_TRUE(a_values.contains(2));
EXPECT_EQ(b_values.size(), 2);
EXPECT_TRUE(b_values.contains(1));
EXPECT_TRUE(b_values.contains(3));
}
a = std::move(b);
/* Want to test self-move. Using std::move twice quiets a compiler warning. */
a = std::move(std::move(a));
{
Set<int> a_values;
for (const int value : a) {
a_values.add(value);
}
Set<int> b_values;
for (const int value : b) {
b_values.add(value);
}
EXPECT_EQ(a_values.size(), 2);
EXPECT_TRUE(a_values.contains(1));
EXPECT_TRUE(a_values.contains(3));
EXPECT_TRUE(b_values.is_empty());
}
}
} // namespace blender::linear_allocator::tests

View File

@ -54,35 +54,13 @@ void BlurNode::convert_to_operations(NodeConverter &converter,
converter.map_input_socket(get_input_socket(1), clamp->get_input_socket(0));
converter.add_link(zero->get_output_socket(), clamp->get_input_socket(1));
GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation();
operationx->set_data(data);
operationx->set_quality(quality);
operationx->set_size(1.0f);
operationx->set_falloff(PROP_SMOOTH);
operationx->set_subtract(false);
operationx->set_extend_bounds(extend_bounds);
converter.add_operation(operationx);
converter.add_link(clamp->get_output_socket(), operationx->get_input_socket(0));
GaussianAlphaYBlurOperation *operationy = new GaussianAlphaYBlurOperation();
operationy->set_data(data);
operationy->set_quality(quality);
operationy->set_size(1.0f);
operationy->set_falloff(PROP_SMOOTH);
operationy->set_subtract(false);
operationy->set_extend_bounds(extend_bounds);
converter.add_operation(operationy);
converter.add_link(operationx->get_output_socket(), operationy->get_input_socket(0));
GaussianBlurReferenceOperation *operation = new GaussianBlurReferenceOperation();
operation->set_data(data);
operation->set_quality(quality);
operation->set_extend_bounds(extend_bounds);
converter.add_operation(operation);
converter.add_link(operationy->get_output_socket(), operation->get_input_socket(1));
converter.add_link(clamp->get_output_socket(), operation->get_input_socket(1));
output_operation = operation;
input_operation = operation;

View File

@ -26,7 +26,9 @@ class ConstantOperation : public NodeOperation {
virtual const float *get_constant_elem() = 0;
bool can_get_constant_elem() const;
void update_memory_buffer(MemoryBuffer *output, const rcti &area, Span<MemoryBuffer *> inputs);
void update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@ -2,6 +2,11 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <memory>
#include "BLI_index_range.hh"
#include "BLI_math_vector.hh"
#include "COM_GaussianBokehBlurOperation.h"
#include "RE_pipeline.h"
@ -148,7 +153,7 @@ void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *outp
GaussianBlurReferenceOperation::GaussianBlurReferenceOperation()
: BlurBaseOperation(DataType::Color)
{
maintabs_ = nullptr;
weights_ = nullptr;
use_variable_size_ = true;
}
@ -206,23 +211,45 @@ void GaussianBlurReferenceOperation::init_execution()
void GaussianBlurReferenceOperation::update_gauss()
{
int i;
int x = std::max(filtersizex_, filtersizey_);
maintabs_ = (float **)MEM_mallocN(x * sizeof(float *), "gauss array");
for (i = 0; i < x; i++) {
maintabs_[i] = make_gausstab(i + 1, i + 1);
}
}
const int2 radius = int2(filtersizex_, filtersizey_);
const float2 scale = math::safe_divide(float2(1.0f), float2(radius));
const int2 size = radius + int2(1);
void GaussianBlurReferenceOperation::deinit_execution()
{
int x, i;
x = std::max(filtersizex_, filtersizey_);
for (i = 0; i < x; i++) {
MEM_freeN(maintabs_[i]);
rcti weights_area;
BLI_rcti_init(&weights_area, 0, size.x, 0, size.y);
weights_ = std::make_unique<MemoryBuffer>(DataType::Value, weights_area, false);
float sum = 0.0f;
const float center_weight = RE_filter_value(data_.filtertype, 0.0f);
*weights_->get_elem(0, 0) = center_weight;
sum += center_weight;
for (const int x : IndexRange(size.x).drop_front(1)) {
const float weight = RE_filter_value(data_.filtertype, x * scale.x);
*weights_->get_elem(x, 0) = weight;
sum += weight * 2.0f;
}
for (const int y : IndexRange(size.y).drop_front(1)) {
const float weight = RE_filter_value(data_.filtertype, y * scale.y);
*weights_->get_elem(0, y) = weight;
sum += weight * 2.0f;
}
for (const int y : IndexRange(size.y).drop_front(1)) {
for (const int x : IndexRange(size.x).drop_front(1)) {
const float weight = RE_filter_value(data_.filtertype, math::length(float2(x, y) * scale));
*weights_->get_elem(x, y) = weight;
sum += weight * 4.0f;
}
}
for (const int y : IndexRange(size.y)) {
for (const int x : IndexRange(size.x)) {
*weights_->get_elem(x, y) /= sum;
}
}
MEM_freeN(maintabs_);
BlurBaseOperation::deinit_execution();
}
void GaussianBlurReferenceOperation::get_area_of_interest(const int input_idx,
@ -246,56 +273,56 @@ void GaussianBlurReferenceOperation::update_memory_buffer_partial(MemoryBuffer *
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *size_input = inputs[SIZE_INPUT_INDEX];
const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX];
MemoryBuffer *size_input = inputs[SIZE_INPUT_INDEX];
for (BuffersIterator<float> it = output->iterate_with({size_input}, area); !it.is_end(); ++it) {
const float ref_size = *it.in(0);
int ref_radx = int(ref_size * radx_);
int ref_rady = int(ref_size * rady_);
if (ref_radx > filtersizex_) {
ref_radx = filtersizex_;
}
else if (ref_radx < 1) {
ref_radx = 1;
}
if (ref_rady > filtersizey_) {
ref_rady = filtersizey_;
}
else if (ref_rady < 1) {
ref_rady = 1;
int2 weights_size = int2(weights_->get_width(), weights_->get_height());
int2 base_radius = weights_size - int2(1);
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
float4 accumulated_color = float4(0.0f);
float4 accumulated_weight = float4(0.0f);
int2 radius = int2(math::ceil(float2(base_radius) * *size_input->get_elem(it.x, it.y)));
float4 center_color = float4(image_input->get_elem_clamped(it.x, it.y));
float center_weight = *weights_->get_elem(0, 0);
accumulated_color += center_color * center_weight;
accumulated_weight += center_weight;
for (int x = 1; x <= radius.x; x++) {
float weight_coordinates = (x / float(radius.x)) * base_radius.x;
float weight;
weights_->read_elem_bilinear(weight_coordinates, 0.0f, &weight);
accumulated_color += float4(image_input->get_elem_clamped(it.x + x, it.y)) * weight;
accumulated_color += float4(image_input->get_elem_clamped(it.x - x, it.y)) * weight;
accumulated_weight += weight * 2.0f;
}
const int x = it.x;
const int y = it.y;
if (ref_radx == 1 && ref_rady == 1) {
image_input->read_elem(x, y, it.out);
continue;
for (int y = 1; y <= radius.y; y++) {
float weight_coordinates = (y / float(radius.y)) * base_radius.y;
float weight;
weights_->read_elem_bilinear(0.0f, weight_coordinates, &weight);
accumulated_color += float4(image_input->get_elem_clamped(it.x, it.y + y)) * weight;
accumulated_color += float4(image_input->get_elem_clamped(it.x, it.y - y)) * weight;
accumulated_weight += weight * 2.0f;
}
const int w = get_width();
const int height = get_height();
const int minxr = x - ref_radx < 0 ? -x : -ref_radx;
const int maxxr = x + ref_radx > w ? w - x : ref_radx;
const int minyr = y - ref_rady < 0 ? -y : -ref_rady;
const int maxyr = y + ref_rady > height ? height - y : ref_rady;
const float *gausstabx = maintabs_[ref_radx - 1];
const float *gausstabcentx = gausstabx + ref_radx;
const float *gausstaby = maintabs_[ref_rady - 1];
const float *gausstabcenty = gausstaby + ref_rady;
float gauss_sum = 0.0f;
float color_sum[4] = {0};
const float *row_color = image_input->get_elem(x + minxr, y + minyr);
for (int i = minyr; i < maxyr; i++, row_color += image_input->row_stride) {
const float *color = row_color;
for (int j = minxr; j < maxxr; j++, color += image_input->elem_stride) {
const float val = gausstabcenty[i] * gausstabcentx[j];
gauss_sum += val;
madd_v4_v4fl(color_sum, color, val);
for (int y = 1; y <= radius.y; y++) {
for (int x = 1; x <= radius.x; x++) {
float2 weight_coordinates = (float2(x, y) / float2(radius)) * float2(base_radius);
float weight;
weights_->read_elem_bilinear(weight_coordinates.x, weight_coordinates.y, &weight);
accumulated_color += float4(image_input->get_elem_clamped(it.x + x, it.y + y)) * weight;
accumulated_color += float4(image_input->get_elem_clamped(it.x - x, it.y + y)) * weight;
accumulated_color += float4(image_input->get_elem_clamped(it.x + x, it.y - y)) * weight;
accumulated_color += float4(image_input->get_elem_clamped(it.x - x, it.y - y)) * weight;
accumulated_weight += weight * 4.0f;
}
}
mul_v4_v4fl(it.out, color_sum, 1.0f / gauss_sum);
accumulated_color = math::safe_divide(accumulated_color, accumulated_weight);
copy_v4_v4(it.out, accumulated_color);
}
}

View File

@ -4,6 +4,8 @@
#pragma once
#include <memory>
#include "COM_BlurBaseOperation.h"
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
@ -32,7 +34,7 @@ class GaussianBokehBlurOperation : public BlurBaseOperation {
class GaussianBlurReferenceOperation : public BlurBaseOperation {
private:
float **maintabs_;
std::unique_ptr<MemoryBuffer> weights_;
void update_gauss();
int filtersizex_;
@ -45,8 +47,6 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation {
void init_data() override;
void init_execution() override;
void deinit_execution() override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,

View File

@ -135,10 +135,6 @@ class AssetCatalogSelectorTree : public ui::AbstractTreeView {
uiLayoutSetEmboss(&row, UI_EMBOSS);
if (!is_collapsible()) {
uiItemL(&row, nullptr, ICON_BLANK1);
}
uiLayout *subrow = uiLayoutRow(&row, false);
uiLayoutSetActive(subrow, catalog_path_enabled_);
uiItemL(subrow, catalog_item_.get_name().c_str(), ICON_NONE);

View File

@ -120,6 +120,19 @@ if(WIN32)
add_definitions(-DNOMINMAX)
endif()
if(WITH_GTESTS)
set(TEST_SRC
sculpt_detail_test.cc
)
set(TEST_INC
)
set(TEST_LIB
${LIB}
bf_rna # RNA_prototypes.h
)
blender_add_test_suite_lib(editor_sculpt_paint "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${TEST_LIB}")
endif()
blender_add_lib(bf_editor_sculpt_paint "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# RNA_prototypes.h

View File

@ -5515,17 +5515,19 @@ static void sculpt_stroke_update_step(bContext *C,
sculpt_restore_mesh(sd, ob);
if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) {
float object_space_constant_detail = 1.0f / (sd->constant_detail *
mat4_to_scale(ob->object_to_world().ptr()));
BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail);
BKE_pbvh_bmesh_detail_size_set(
ss->pbvh, dyntopo::detail_size::constant_to_detail_size(sd->constant_detail, ob));
}
else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) {
BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f);
BKE_pbvh_bmesh_detail_size_set(
ss->pbvh,
dyntopo::detail_size::brush_to_detail_size(sd->detail_percent, ss->cache->radius));
}
else {
BKE_pbvh_bmesh_detail_size_set(ss->pbvh,
(ss->cache->radius / ss->cache->dyntopo_pixel_radius) *
(sd->detail_size * U.pixelsize) / 0.4f);
BKE_pbvh_bmesh_detail_size_set(
ss->pbvh,
dyntopo::detail_size::relative_to_detail_size(
sd->detail_size, ss->cache->radius, ss->cache->dyntopo_pixel_radius, U.pixelsize));
}
if (dyntopo::stroke_is_dyntopo(ss, brush)) {

View File

@ -454,17 +454,16 @@ static void dyntopo_detail_size_parallel_lines_draw(uint pos3d,
{
float object_space_constant_detail;
if (cd->mode == DETAILING_MODE_RESOLUTION) {
object_space_constant_detail = 1.0f /
(cd->current_value *
mat4_to_scale(cd->active_object->object_to_world().ptr()));
object_space_constant_detail = detail_size::constant_to_detail_size(cd->current_value,
cd->active_object);
}
else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
object_space_constant_detail = cd->brush_radius * cd->current_value / 100.0f;
object_space_constant_detail = detail_size::brush_to_detail_size(cd->current_value,
cd->brush_radius);
}
else {
object_space_constant_detail = (cd->brush_radius / cd->pixel_radius) *
(cd->current_value * U.pixelsize) /
detail_size::RELATIVE_SCALE_FACTOR;
object_space_constant_detail = detail_size::relative_to_detail_size(
cd->current_value, cd->brush_radius, cd->pixel_radius, U.pixelsize);
}
/* The constant detail represents the maximum edge length allowed before subdividing it. If the
@ -614,7 +613,7 @@ static void dyntopo_detail_size_sample_from_surface(Object *ob,
}
else {
sampled_value = detail_size::constant_to_relative_detail(
detail_size, cd->brush_radius, cd->pixel_radius, cd->active_object);
detail_size, cd->brush_radius, cd->pixel_radius, U.pixelsize, cd->active_object);
}
cd->current_value = clamp_f(sampled_value, cd->min_value, cd->max_value);
}
@ -870,6 +869,24 @@ void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot)
} // namespace blender::ed::sculpt_paint::dyntopo
namespace blender::ed::sculpt_paint::dyntopo::detail_size {
float constant_to_detail_size(const float constant_detail, const Object *ob)
{
return 1.0f / (constant_detail * mat4_to_scale(ob->object_to_world().ptr()));
}
float brush_to_detail_size(const float brush_percent, const float brush_radius)
{
return brush_radius * brush_percent / 100.0f;
}
float relative_to_detail_size(const float relative_detail,
const float brush_radius,
const float pixel_radius,
const float pixel_size)
{
return (brush_radius / pixel_radius) * (relative_detail * pixel_size) / RELATIVE_SCALE_FACTOR;
}
float constant_to_brush_detail(const float constant_detail,
const float brush_radius,
const Object *ob)
@ -882,11 +899,12 @@ float constant_to_brush_detail(const float constant_detail,
float constant_to_relative_detail(const float constant_detail,
const float brush_radius,
const float pixel_radius,
const float pixel_size,
const Object *ob)
{
const float object_scale = mat4_to_scale(ob->object_to_world().ptr());
return (pixel_radius / brush_radius) * (RELATIVE_SCALE_FACTOR / U.pixelsize) *
return (pixel_radius / brush_radius) * (RELATIVE_SCALE_FACTOR / pixel_size) *
(1.0f / (constant_detail * object_scale));
}
} // namespace blender::ed::sculpt_paint::dyntopo::detail_size

View File

@ -0,0 +1,43 @@
#include "sculpt_intern.hh"
#include "BKE_object_types.hh"
#include "testing/testing.h"
namespace blender::ed::sculpt_paint::dyntopo::detail_size::test {
constexpr float CONSTANT_DETAIL = 50.0f;
constexpr float BRUSH_RADIUS = 0.5f;
constexpr float PIXEL_RADIUS = 200;
constexpr float PIXEL_SIZE = 100;
TEST(Conversion, ConstantToBrushDetail)
{
blender::bke::ObjectRuntime runtime;
runtime.object_to_world = MatBase<float, 4, 4>::identity();
Object ob;
ob.runtime = &runtime;
const float brush_percent = constant_to_brush_detail(CONSTANT_DETAIL, BRUSH_RADIUS, &ob);
const float converted = brush_to_detail_size(brush_percent, BRUSH_RADIUS);
const float expected = constant_to_detail_size(CONSTANT_DETAIL, &ob);
EXPECT_FLOAT_EQ(expected, converted);
}
TEST(Conversion, ConstantToRelativeDetail)
{
blender::bke::ObjectRuntime runtime;
runtime.object_to_world = MatBase<float, 4, 4>::identity();
Object ob;
ob.runtime = &runtime;
const float relative_detail = constant_to_relative_detail(
CONSTANT_DETAIL, BRUSH_RADIUS, PIXEL_RADIUS, PIXEL_SIZE, &ob);
const float converted = relative_to_detail_size(
relative_detail, BRUSH_RADIUS, PIXEL_RADIUS, PIXEL_SIZE);
const float expected = constant_to_detail_size(CONSTANT_DETAIL, &ob);
EXPECT_FLOAT_EQ(expected, converted);
}
} // namespace blender::ed::sculpt_paint::dyntopo::detail_size::test

View File

@ -1230,8 +1230,30 @@ void triangulate(BMesh *bm);
WarnFlag check_attribute_warning(Scene *scene, Object *ob);
namespace detail_size {
/**
* Scaling factor to match the displayed size to the actual sculpted size
*/
constexpr float RELATIVE_SCALE_FACTOR = 0.4f;
/**
* Converts from Sculpt#constant_detail to the PBVH max edge length.
*/
float constant_to_detail_size(const float constant_detail, const Object *ob);
/**
* Converts from Sculpt#detail_percent to the PBVH max edge length.
*/
float brush_to_detail_size(const float brush_percent, const float brush_radius);
/**
* Converts from Sculpt#detail_size to the PBVH max edge length.
*/
float relative_to_detail_size(const float relative_detail,
const float brush_radius,
const float pixel_radius,
const float pixel_size);
/**
* Converts from Sculpt#constant_detail to equivalent Sculpt#detail_percent value.
*
@ -1249,6 +1271,7 @@ float constant_to_brush_detail(const float constant_detail,
float constant_to_relative_detail(const float constant_detail,
const float brush_radius,
const float pixel_radius,
const float pixel_size,
const Object *ob);
}
}

View File

@ -370,6 +370,7 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op)
container,
codec,
bitrate,
AUD_RESAMPLE_QUALITY_MEDIUM,
nullptr,
nullptr,
error_message,
@ -385,6 +386,7 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op)
container,
codec,
bitrate,
AUD_RESAMPLE_QUALITY_MEDIUM,
nullptr,
nullptr,
error_message,

View File

@ -48,23 +48,13 @@
/** \name Channel List
* \{ */
void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region)
void draw_channel_names(bContext *C,
bAnimContext *ac,
ARegion *region,
const ListBase /* bAnimListElem */ &anim_data)
{
ListBase anim_data = {nullptr, nullptr};
bAnimListElem *ale;
eAnimFilter_Flags filter;
View2D *v2d = &region->v2d;
size_t items;
/* build list of channels to draw */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, eAnimCont_Types(ac->datatype));
const int height = ANIM_UI_get_channels_total_height(v2d, items);
const float pad_bottom = BLI_listbase_is_empty(ac->markers) ? 0 : UI_MARKER_MARGIN_Y;
v2d->tot.ymin = -(height + pad_bottom);
/* need to do a view-sync here, so that the keys area doesn't jump around (it must copy this) */
UI_view2d_sync(nullptr, ac->area, v2d, V2D_LOCK_COPY);
@ -112,9 +102,6 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region)
UI_block_end(C, block);
UI_block_draw(C, block);
}
/* Free temporary channels. */
ANIM_animdata_freelist(&anim_data);
}
/** \} */

View File

@ -30,7 +30,10 @@ void action_buttons_register(ARegionType *art);
/**
* Left hand part.
*/
void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region);
void draw_channel_names(bContext *C,
bAnimContext *ac,
ARegion *region,
const ListBase /* bAnimListElem */ &anim_data);
/**
* Draw keyframes in each channel.
*/

View File

@ -268,21 +268,39 @@ static void action_channel_region_init(wmWindowManager *wm, ARegion *region)
WM_event_add_keymap_handler(&region->handlers, keymap);
}
static void set_v2d_height(View2D *v2d, const size_t item_count, const bool add_marker_padding)
{
const int height = ANIM_UI_get_channels_total_height(v2d, item_count);
const float pad_bottom = add_marker_padding ? UI_MARKER_MARGIN_Y : 0;
v2d->tot.ymin = -(height + pad_bottom);
UI_view2d_curRect_clamp_y(v2d);
}
static void action_channel_region_draw(const bContext *C, ARegion *region)
{
/* draw entirely, view changes should be handled here */
bAnimContext ac;
if (!ANIM_animdata_get_context(C, &ac)) {
return;
}
View2D *v2d = &region->v2d;
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
UI_view2d_view_ortho(v2d);
ListBase anim_data = {nullptr, nullptr};
/* Build list of channels to draw. */
const eAnimFilter_Flags filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
ANIMFILTER_LIST_CHANNELS);
const size_t item_count = ANIM_animdata_filter(
&ac, &anim_data, filter, ac.data, eAnimCont_Types(ac.datatype));
/* The View2D's height needs to be set before calling UI_view2d_view_ortho because the latter
* uses the View2D's `cur` rect which might be modified when setting the height. */
set_v2d_height(v2d, item_count, !BLI_listbase_is_empty(ac.markers));
/* data */
if (ANIM_animdata_get_context(C, &ac)) {
draw_channel_names((bContext *)C, &ac, region);
}
UI_view2d_view_ortho(v2d);
draw_channel_names((bContext *)C, &ac, region, anim_data);
/* channel filter next to scrubbing area */
ED_time_scrub_channel_search_draw(C, region, ac.ads);
@ -291,6 +309,7 @@ static void action_channel_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
/* no scrollers here */
ANIM_animdata_freelist(&anim_data);
}
/* add handlers, stuff you only do once or on area/region changes */
@ -856,12 +875,6 @@ static void action_space_blend_write(BlendWriter *writer, SpaceLink *sl)
BLO_write_struct(writer, SpaceAction, sl);
}
static void action_main_region_view2d_changed(const bContext * /*C*/, ARegion *region)
{
View2D *v2d = &region->v2d;
UI_view2d_curRect_clamp_y(v2d);
}
void ED_spacetype_action()
{
std::unique_ptr<SpaceType> st = std::make_unique<SpaceType>();
@ -895,7 +908,6 @@ void ED_spacetype_action()
art->draw_overlay = action_main_region_draw_overlay;
art->listener = action_main_region_listener;
art->message_subscribe = saction_main_region_message_subscribe;
art->on_view2d_changed = action_main_region_view2d_changed;
art->keymapflag = ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
BLI_addhead(&st->regiontypes, art);

View File

@ -1517,27 +1517,15 @@ void graph_draw_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, shor
/** \name Channel List
* \{ */
void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region)
void graph_draw_channel_names(bContext *C,
bAnimContext *ac,
ARegion *region,
const ListBase /* bAnimListElem */ &anim_data)
{
ListBase anim_data = {nullptr, nullptr};
bAnimListElem *ale;
int filter;
View2D *v2d = &region->v2d;
float height;
size_t items;
/* build list of channels to draw */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
ANIMFILTER_FCURVESONLY);
items = ANIM_animdata_filter(
ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
/* Update max-extent of channels here (taking into account scrollers):
* - this is done to allow the channel list to be scrollable, but must be done here
* to avoid regenerating the list again and/or also because channels list is drawn first */
height = ANIM_UI_get_channels_total_height(v2d, items);
v2d->tot.ymin = -height;
const float channel_step = ANIM_UI_get_channel_step();
/* Loop through channels, and set up drawing depending on their type. */
@ -1588,9 +1576,6 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region)
GPU_blend(GPU_BLEND_NONE);
}
/* Free temporary channels. */
ANIM_animdata_freelist(&anim_data);
}
/** \} */

View File

@ -27,7 +27,10 @@ extern "C" {
/**
* Left hand part.
*/
void graph_draw_channel_names(struct bContext *C, struct bAnimContext *ac, struct ARegion *region);
void graph_draw_channel_names(struct bContext *C,
struct bAnimContext *ac,
struct ARegion *region,
const ListBase /* bAnimListElem */ &anim_data);
/**
* This is called twice from `space_graph.cc`, #graph_main_region_draw()

View File

@ -367,20 +367,34 @@ static void graph_channel_region_init(wmWindowManager *wm, ARegion *region)
WM_event_add_keymap_handler(&region->handlers, keymap);
}
static void set_v2d_height(View2D *v2d, const size_t item_count)
{
const int height = ANIM_UI_get_channels_total_height(v2d, item_count);
v2d->tot.ymin = -height;
UI_view2d_curRect_clamp_y(v2d);
}
static void graph_channel_region_draw(const bContext *C, ARegion *region)
{
bAnimContext ac;
if (!ANIM_animdata_get_context(C, &ac)) {
return;
}
View2D *v2d = &region->v2d;
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
ListBase anim_data = {nullptr, nullptr};
const eAnimFilter_Flags filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FCURVESONLY);
const size_t item_count = ANIM_animdata_filter(
&ac, &anim_data, filter, ac.data, eAnimCont_Types(ac.datatype));
set_v2d_height(v2d, item_count);
UI_view2d_view_ortho(v2d);
/* draw channels */
if (ANIM_animdata_get_context(C, &ac)) {
graph_draw_channel_names((bContext *)C, &ac, region);
}
graph_draw_channel_names((bContext *)C, &ac, region, anim_data);
/* channel filter next to scrubbing area */
ED_time_scrub_channel_search_draw(C, region, ac.ads);
@ -390,6 +404,8 @@ static void graph_channel_region_draw(const bContext *C, ARegion *region)
/* scrollers */
UI_view2d_scrollers_draw(v2d, nullptr);
ANIM_animdata_freelist(&anim_data);
}
/* add handlers, stuff you only do once or on area/region changes */

View File

@ -909,27 +909,14 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
/* *********************************************** */
/* Track List */
void draw_nla_track_list(const bContext *C, bAnimContext *ac, ARegion *region)
void draw_nla_track_list(const bContext *C,
bAnimContext *ac,
ARegion *region,
const ListBase /* bAnimListElem */ &anim_data)
{
ListBase anim_data = {nullptr, nullptr};
SpaceNla *snla = reinterpret_cast<SpaceNla *>(ac->sl);
View2D *v2d = &region->v2d;
size_t items;
/* build list of tracks to draw */
eAnimFilter_Flags filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FCURVESONLY);
items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, eAnimCont_Types(ac->datatype));
/* Update max-extent of tracks here (taking into account scrollers):
* - this is done to allow the track list to be scrollable, but must be done here
* to avoid regenerating the list again and/or also because tracks list is drawn first
* - offset of NLATRACK_HEIGHT*2 is added to the height of the tracks, as first is for
* start of list offset, and the second is as a correction for the scrollers.
*/
int height = NLATRACK_TOT_HEIGHT(ac, items);
v2d->tot.ymin = -height;
/* need to do a view-sync here, so that the keys area doesn't jump around
* (it must copy this) */
@ -984,9 +971,6 @@ void draw_nla_track_list(const bContext *C, bAnimContext *ac, ARegion *region)
GPU_blend(GPU_BLEND_NONE);
}
/* free temporary tracks */
ANIM_animdata_freelist(&anim_data);
}
/* *********************************************** */

View File

@ -27,7 +27,10 @@ void nla_buttons_register(ARegionType *art);
/* `nla_draw.cc` */
void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region);
void draw_nla_track_list(const bContext *C, bAnimContext *ac, ARegion *region);
void draw_nla_track_list(const bContext *C,
bAnimContext *ac,
ARegion *region,
const ListBase /* bAnimListElem */ &anim_data);
/* **************************************** */
/* `nla_select.cc` */

View File

@ -176,17 +176,35 @@ static void nla_track_region_init(wmWindowManager *wm, ARegion *region)
static void nla_track_region_draw(const bContext *C, ARegion *region)
{
bAnimContext ac;
View2D *v2d = &region->v2d;
if (!ANIM_animdata_get_context(C, &ac)) {
return;
}
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
ListBase anim_data = {nullptr, nullptr};
SpaceNla *snla = reinterpret_cast<SpaceNla *>(ac.sl);
View2D *v2d = &region->v2d;
const eAnimFilter_Flags filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
ANIMFILTER_LIST_CHANNELS | ANIMFILTER_FCURVESONLY);
const size_t item_count = ANIM_animdata_filter(
&ac, &anim_data, filter, ac.data, eAnimCont_Types(ac.datatype));
/* Recalculate the height of the track list. Needs to be done before the call to
* `UI_view2d_view_ortho`.*/
int height = NLATRACK_TOT_HEIGHT(&ac, item_count);
if (!BLI_listbase_is_empty(ED_context_get_markers(C))) {
height -= (UI_MARKER_MARGIN_Y - NLATRACK_STEP(snla));
}
v2d->tot.ymin = -height;
UI_view2d_curRect_clamp_y(v2d);
UI_view2d_view_ortho(v2d);
/* data */
if (ANIM_animdata_get_context(C, &ac)) {
draw_nla_track_list(C, &ac, region);
}
draw_nla_track_list(C, &ac, region, anim_data);
/* track filter next to scrubbing area */
ED_time_scrub_channel_search_draw(C, region, ac.ads);
@ -196,6 +214,7 @@ static void nla_track_region_draw(const bContext *C, ARegion *region)
/* scrollers */
UI_view2d_scrollers_draw(v2d, nullptr);
ANIM_animdata_freelist(&anim_data);
}
/* add handlers, stuff you only do once or on area/region changes */
@ -430,20 +449,6 @@ static void nla_main_region_message_subscribe(const wmRegionMessageSubscribePara
}
}
static void nla_main_region_view2d_changed(const bContext *C, ARegion *region)
{
SpaceNla *snla = CTX_wm_space_nla(C);
View2D *v2d = &region->v2d;
/* If markers are present add region padding
* so bottom strip isn't hidden.
*/
if (!BLI_listbase_is_empty(ED_context_get_markers(C))) {
v2d->tot.ymin -= (UI_MARKER_MARGIN_Y - NLATRACK_STEP(snla));
}
UI_view2d_curRect_clamp_y(v2d);
}
static void nla_track_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
@ -626,7 +631,6 @@ void ED_spacetype_nla()
art->draw_overlay = nla_main_region_draw_overlay;
art->listener = nla_main_region_listener;
art->message_subscribe = nla_main_region_message_subscribe;
art->on_view2d_changed = nla_main_region_view2d_changed;
art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;
BLI_addhead(&st->regiontypes, art);

View File

@ -1709,6 +1709,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C,
case CTX_MODE_EDIT_GREASE_PENCIL:
ARRAY_SET_ITEMS(contexts, ".grease_pencil_edit");
break;
case CTX_MODE_PAINT_GREASE_PENCIL:
ARRAY_SET_ITEMS(contexts, ".grease_pencil_paint");
break;
case CTX_MODE_EDIT_POINT_CLOUD:
ARRAY_SET_ITEMS(contexts, ".point_cloud_edit");
break;

View File

@ -468,11 +468,16 @@ static bool view3d_selectable_data(bContext *C)
}
}
else {
if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) &&
if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT)) &&
!BKE_paint_select_elem_test(ob))
{
return false;
}
if ((ob->mode & OB_MODE_WEIGHT_PAINT) &&
!(BKE_paint_select_elem_test(ob) || BKE_object_pose_armature_get_with_wpaint_check(ob)))
{
return false;
}
}
}
@ -543,31 +548,24 @@ static void do_lasso_select_pose__do_tag(void *user_data,
data->is_changed = true;
}
}
static void do_lasso_tag_pose(ViewContext *vc,
Object *ob,
const int mcoords[][2],
const int mcoords_len)
static void do_lasso_tag_pose(ViewContext *vc, const int mcoords[][2], const int mcoords_len)
{
ViewContext vc_tmp;
LassoSelectUserData data;
rcti rect;
if ((ob->type != OB_ARMATURE) || (ob->pose == nullptr)) {
if ((vc->obact->type != OB_ARMATURE) || (vc->obact->pose == nullptr)) {
return;
}
vc_tmp = *vc;
vc_tmp.obact = ob;
BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
view3d_userdata_lassoselect_init(
&data, vc, &rect, mcoords, mcoords_len, static_cast<eSelectOp>(0));
ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d);
ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d);
/* Treat bones as clipped segments (no joints). */
pose_foreachScreenBone(&vc_tmp,
pose_foreachScreenBone(vc,
do_lasso_select_pose__do_tag,
&data,
V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT);
@ -617,22 +615,37 @@ static bool do_lasso_select_objects(ViewContext *vc,
*/
static blender::Vector<Base *> do_pose_tag_select_op_prepare(ViewContext *vc)
{
blender::Vector<Base *> bases;
FOREACH_BASE_IN_MODE_BEGIN (
vc->scene, vc->view_layer, vc->v3d, OB_ARMATURE, OB_MODE_POSE, base_iter)
{
Object *ob_iter = base_iter->object;
bArmature *arm = static_cast<bArmature *>(ob_iter->data);
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_iter->pose->chanbase) {
auto bases_tag_and_append_fn = [](blender::Vector<Base *> &bases, Base *base) {
Object *ob = base->object;
bArmature *arm = static_cast<bArmature *>(ob->data);
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
Bone *bone = pchan->bone;
bone->flag &= ~BONE_DONE;
}
arm->id.tag |= LIB_TAG_DOIT;
ob_iter->id.tag &= ~LIB_TAG_DOIT;
bases.append(base_iter);
ob->id.tag &= ~LIB_TAG_DOIT;
bases.append(base);
};
blender::Vector<Base *> bases;
/* Special case, pose + weight paint mode. */
if (vc->obact && (vc->obact->mode & OB_MODE_WEIGHT_PAINT)) {
Object *ob_pose = BKE_object_pose_armature_get_with_wpaint_check(vc->obact);
BLI_assert(ob_pose != nullptr); /* Caller is expected to check. */
Base *base = BKE_view_layer_base_find(vc->view_layer, ob_pose);
if (base) {
bases_tag_and_append_fn(bases, base);
}
}
else {
FOREACH_BASE_IN_MODE_BEGIN (
vc->scene, vc->view_layer, vc->v3d, OB_ARMATURE, OB_MODE_POSE, base_iter)
{
bases_tag_and_append_fn(bases, base_iter);
}
FOREACH_BASE_IN_MODE_END;
}
FOREACH_BASE_IN_MODE_END;
return bases;
}
@ -697,10 +710,13 @@ static bool do_lasso_select_pose(ViewContext *vc,
{
blender::Vector<Base *> bases = do_pose_tag_select_op_prepare(vc);
ViewContext vc_temp = *vc;
for (const int i : bases.index_range()) {
Base *base_iter = bases[i];
Object *ob_iter = base_iter->object;
do_lasso_tag_pose(vc, ob_iter, mcoords, mcoords_len);
ED_view3d_viewcontext_init_object(&vc_temp, ob_iter);
do_lasso_tag_pose(&vc_temp, mcoords, mcoords_len);
}
const bool changed_multi = do_pose_tag_select_op_exec(bases, sel_op);
@ -1382,20 +1398,23 @@ static bool view3d_lasso_select(bContext *C,
else if (BKE_paint_select_vert_test(ob)) {
changed_multi |= do_lasso_select_paintvert(vc, wm_userdata, mcoords, mcoords_len, sel_op);
}
else if (ob &&
(ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)))
{
/* pass */
}
else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
changed_multi |= PE_lasso_select(C, mcoords, mcoords_len, sel_op) != OPERATOR_CANCELLED;
}
else if (ob && (ob->mode & OB_MODE_POSE)) {
else if (ob &&
((ob->mode & OB_MODE_POSE) | ((ob->mode & OB_MODE_WEIGHT_PAINT) &&
BKE_object_pose_armature_get_with_wpaint_check(ob))))
{
changed_multi |= do_lasso_select_pose(vc, mcoords, mcoords_len, sel_op);
if (changed_multi) {
ED_outliner_select_sync_from_pose_bone_tag(C);
}
}
else if (ob &&
(ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)))
{
/* pass */
}
else {
changed_multi |= do_lasso_select_objects(vc, mcoords, mcoords_len, sel_op);
if (changed_multi) {
@ -4369,7 +4388,10 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
changed_multi = PE_box_select(C, &rect, sel_op);
}
else if (vc.obact && vc.obact->mode & OB_MODE_POSE) {
else if (vc.obact && ((vc.obact->mode & OB_MODE_POSE) ||
((vc.obact->mode & OB_MODE_WEIGHT_PAINT) &&
BKE_object_pose_armature_get_with_wpaint_check(vc.obact))))
{
changed_multi = do_pose_box_select(C, &vc, &rect, sel_op);
if (changed_multi) {
ED_outliner_select_sync_from_pose_bone_tag(C);
@ -5345,6 +5367,14 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
else if (obact && obact->mode & OB_MODE_SCULPT) {
return OPERATOR_CANCELLED;
}
else if (Object *obact_pose = (obact && (obact->mode & OB_MODE_WEIGHT_PAINT)) ?
BKE_object_pose_armature_get_with_wpaint_check(obact) :
nullptr)
{
ED_view3d_viewcontext_init_object(&vc, obact_pose);
pose_circle_select(&vc, sel_op, mval, float(radius));
ED_outliner_select_sync_from_pose_bone_tag(C);
}
else {
if (object_circle_select(&vc, sel_op, mval, float(radius))) {
DEG_id_tag_update(&vc.scene->id, ID_RECALC_SELECT);

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