Curves: Add simplify_curve_attribute function #118560

Merged
Falk David merged 17 commits from filedescriptor/blender:curves-resample-adaptive into main 2024-03-26 15:28:22 +01:00
137 changed files with 2125 additions and 1222 deletions
Showing only changes of commit a1550b287c - Show all commits

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

@ -931,7 +931,8 @@ static void export_hair_curves_motion(Hair *hair,
}
/* Export motion keys. */
const int num_keys = hair->get_curve_keys().size();
const size_t num_keys = hair->num_keys();
const size_t num_curves = hair->num_curves();
float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
bool have_motion = false;
int num_motion_keys = 0;
@ -944,6 +945,9 @@ static void export_hair_curves_motion(Hair *hair,
for (const int i : points_by_curve.index_range()) {
const blender::IndexRange points = points_by_curve[i];
if (curve_index >= num_curves) {
break;
}
Hair::Curve curve = hair->get_curve(curve_index);
curve_index++;

View File

@ -661,9 +661,7 @@ void HIPDevice::tex_alloc(device_texture &mem)
address_mode = hipAddressModeClamp;
break;
case EXTENSION_CLIP:
/* TODO(@arya): setting this to Mode Clamp instead of Mode Border
* because it's unsupported in HIP. */
address_mode = hipAddressModeClamp;
address_mode = hipAddressModeBorder;
break;
case EXTENSION_MIRROR:
address_mode = hipAddressModeMirror;
@ -855,7 +853,11 @@ void HIPDevice::tex_alloc(device_texture &mem)
thread_scoped_lock lock(device_mem_map_mutex);
cmem = &device_mem_map[&mem];
hip_assert(hipTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL));
if (hipTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL) != hipSuccess) {
set_error(
"Failed to create texture. Maximum GPU texture size or available GPU memory was likely "
"exceeded.");
}
texture_info[slot].data = (uint64_t)cmem->texobject;
}

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

@ -118,6 +118,7 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel):
col.prop(light, "use_shadow", text="Cast Shadow")
col.prop(light, "shadow_softness_factor", text="Shadow Softness")
col.prop(light, "shadow_filter_radius", text="Filtering Radius")
if light.type == 'SUN':
col.prop(light, "shadow_trace_distance", text="Trace Distance")

View File

@ -1394,6 +1394,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

@ -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

@ -2490,6 +2490,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,
@ -2581,6 +2765,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

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 7
#define BLENDER_FILE_SUBVERSION 8
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@ -15,6 +15,8 @@ struct GreasePencilDrawing;
struct ListBase;
struct Main;
struct Object;
struct LineartGpencilModifierData;
struct GreasePencilLineartModifierData;
namespace blender::bke::greasepencil::convert {
@ -30,4 +32,9 @@ void layer_adjustments_to_modifiers(Main &bmain,
const bGPdata &src_object_data,
Object &dst_object);
void lineart_wrap_v3(const LineartGpencilModifierData *lmd_legacy,
GreasePencilLineartModifierData *lmd);
void lineart_unwrap_v3(LineartGpencilModifierData *lmd_legacy,
const GreasePencilLineartModifierData *lmd);
} // namespace blender::bke::greasepencil::convert

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

@ -5319,6 +5319,45 @@ static void write_grid_paint_mask(BlendWriter *writer,
}
}
static void blend_write_layer_data(BlendWriter *writer,
const CustomDataLayer &layer,
const int count)
{
switch (layer.type) {
case CD_MDEFORMVERT:
BKE_defvert_blend_write(writer, count, static_cast<const MDeformVert *>(layer.data));
break;
case CD_MDISPS:
write_mdisps(
writer, count, static_cast<const MDisps *>(layer.data), layer.flag & CD_FLAG_EXTERNAL);
break;
case CD_PAINT_MASK:
BLO_write_raw(writer, sizeof(float) * count, static_cast<const float *>(layer.data));
break;
case CD_GRID_PAINT_MASK:
write_grid_paint_mask(writer, count, static_cast<const GridPaintMask *>(layer.data));
break;
case CD_PROP_BOOL:
BLO_write_raw(writer, sizeof(bool) * count, static_cast<const bool *>(layer.data));
break;
default: {
const char *structname;
int structnum;
CustomData_file_write_info(eCustomDataType(layer.type), &structname, &structnum);
if (structnum) {
int datasize = structnum * count;
BLO_write_struct_array_by_name(writer, structname, datasize, layer.data);
}
else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */
printf("%s error: layer '%s':%d - can't be written to file\n",
__func__,
structname,
layer.type);
}
}
}
}
void CustomData_blend_write(BlendWriter *writer,
CustomData *data,
Span<CustomDataLayer> layers_to_write,
@ -5335,39 +5374,7 @@ void CustomData_blend_write(BlendWriter *writer,
writer, CustomDataLayer, data->totlayer, data->layers, layers_to_write.data());
for (const CustomDataLayer &layer : layers_to_write) {
switch (layer.type) {
case CD_MDEFORMVERT:
BKE_defvert_blend_write(writer, count, static_cast<const MDeformVert *>(layer.data));
break;
case CD_MDISPS:
write_mdisps(
writer, count, static_cast<const MDisps *>(layer.data), layer.flag & CD_FLAG_EXTERNAL);
break;
case CD_PAINT_MASK:
BLO_write_raw(writer, sizeof(float) * count, static_cast<const float *>(layer.data));
break;
case CD_GRID_PAINT_MASK:
write_grid_paint_mask(writer, count, static_cast<const GridPaintMask *>(layer.data));
break;
case CD_PROP_BOOL:
BLO_write_raw(writer, sizeof(bool) * count, static_cast<const bool *>(layer.data));
break;
default: {
const char *structname;
int structnum;
CustomData_file_write_info(eCustomDataType(layer.type), &structname, &structnum);
if (structnum) {
int datasize = structnum * count;
BLO_write_struct_array_by_name(writer, structname, datasize, layer.data);
}
else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */
printf("%s error: layer '%s':%d - can't be written to file\n",
__func__,
structname,
layer.type);
}
}
}
blend_write_layer_data(writer, layer, count);
}
if (data->external) {
@ -5420,6 +5427,35 @@ static void blend_read_paint_mask(BlendDataReader *reader,
}
}
static void blend_read_layer_data(BlendDataReader *reader, CustomDataLayer &layer, const int count)
{
BLO_read_data_address(reader, &layer.data);
if (layer.data != nullptr) {
/* Make layer data shareable. */
layer.sharing_info = make_implicit_sharing_info_for_layer(
eCustomDataType(layer.type), layer.data, count);
}
if (CustomData_layer_ensure_data_exists(&layer, count)) {
/* Under normal operations, this shouldn't happen, but...
* For a CD_PROP_BOOL example, see #84935.
* For a CD_MLOOPUV example, see #90620. */
CLOG_WARN(&LOG,
"Allocated custom data layer that was not saved correctly for layer.type = %d.",
layer.type);
}
if (layer.type == CD_MDISPS) {
blend_read_mdisps(
reader, count, static_cast<MDisps *>(layer.data), layer.flag & CD_FLAG_EXTERNAL);
}
else if (layer.type == CD_GRID_PAINT_MASK) {
blend_read_paint_mask(reader, count, static_cast<GridPaintMask *>(layer.data));
}
else if (layer.type == CD_MDEFORMVERT) {
BKE_defvert_blend_read(reader, count, static_cast<MDeformVert *>(layer.data));
}
}
void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int count)
{
BLO_read_data_address(reader, &data->layers);
@ -5443,31 +5479,7 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int
layer->sharing_info = nullptr;
if (CustomData_verify_versions(data, i)) {
BLO_read_data_address(reader, &layer->data);
if (layer->data != nullptr) {
/* Make layer data shareable. */
layer->sharing_info = make_implicit_sharing_info_for_layer(
eCustomDataType(layer->type), layer->data, count);
}
if (CustomData_layer_ensure_data_exists(layer, count)) {
/* Under normal operations, this shouldn't happen, but...
* For a CD_PROP_BOOL example, see #84935.
* For a CD_MLOOPUV example, see #90620. */
CLOG_WARN(&LOG,
"Allocated custom data layer that was not saved correctly for layer->type = %d.",
layer->type);
}
if (layer->type == CD_MDISPS) {
blend_read_mdisps(
reader, count, static_cast<MDisps *>(layer->data), layer->flag & CD_FLAG_EXTERNAL);
}
else if (layer->type == CD_GRID_PAINT_MASK) {
blend_read_paint_mask(reader, count, static_cast<GridPaintMask *>(layer->data));
}
else if (layer->type == CD_MDEFORMVERT) {
BKE_defvert_blend_read(reader, count, static_cast<MDeformVert *>(layer->data));
}
blend_read_layer_data(reader, *layer, count);
i++;
}
}

View File

@ -49,6 +49,7 @@
#include "DNA_ID.h"
#include "DNA_ID_enums.h"
#include "DNA_brush_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_grease_pencil_types.h"
#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
@ -2769,5 +2770,3 @@ static void write_layer_tree(GreasePencil &grease_pencil, BlendWriter *writer)
grease_pencil.root_group_ptr->wrap().prepare_for_dna_write();
write_layer_tree_group(writer, grease_pencil.root_group_ptr);
}
/** \} */

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;
@ -1594,6 +1598,16 @@ static void legacy_object_modifier_weight_proximity(Object &object, GpencilModif
false);
}
static void legacy_object_modifier_weight_lineart(Object &object, GpencilModifierData &legacy_md)
{
ModifierData &md = legacy_object_modifier_common(
object, eModifierType_GreasePencilWeightAngle, legacy_md);
auto &md_lineart = reinterpret_cast<GreasePencilLineartModifierData &>(md);
auto &legacy_md_lineart = reinterpret_cast<LineartGpencilModifierData &>(legacy_md);
greasepencil::convert::lineart_wrap_v3(&legacy_md_lineart, &md_lineart);
}
static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
{
BLI_assert(BLI_listbase_is_empty(&object.modifiers));
@ -1667,6 +1681,8 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
case eGpencilModifierType_Simplify:
case eGpencilModifierType_Texture:
case eGpencilModifierType_Lineart:
legacy_object_modifier_weight_lineart(object, *gpd_md);
break;
case eGpencilModifierType_Shrinkwrap:
case eGpencilModifierType_Envelope:
case eGpencilModifierType_Outline:
@ -1703,4 +1719,98 @@ void legacy_gpencil_object(Main &bmain, Object &object)
BKE_object_free_derived_caches(&object);
}
void lineart_wrap_v3(const LineartGpencilModifierData *lmd_legacy,
GreasePencilLineartModifierData *lmd)
{
#define LMD_WRAP(var) lmd->var = lmd_legacy->var
LMD_WRAP(edge_types);
LMD_WRAP(source_type);
LMD_WRAP(use_multiple_levels);
LMD_WRAP(level_start);
LMD_WRAP(level_end);
LMD_WRAP(source_camera);
LMD_WRAP(light_contour_object);
LMD_WRAP(source_object);
LMD_WRAP(source_collection);
LMD_WRAP(target_material);
STRNCPY(lmd->source_vertex_group, lmd_legacy->source_vertex_group);
STRNCPY(lmd->vgname, lmd_legacy->vgname);
LMD_WRAP(overscan);
LMD_WRAP(shadow_camera_fov);
LMD_WRAP(shadow_camera_size);
LMD_WRAP(shadow_camera_near);
LMD_WRAP(shadow_camera_far);
LMD_WRAP(opacity);
lmd->thickness = lmd_legacy->thickness / 2;
LMD_WRAP(mask_switches);
LMD_WRAP(material_mask_bits);
LMD_WRAP(intersection_mask);
LMD_WRAP(shadow_selection);
LMD_WRAP(silhouette_selection);
LMD_WRAP(crease_threshold);
LMD_WRAP(angle_splitting_threshold);
LMD_WRAP(chain_smooth_tolerance);
LMD_WRAP(chaining_image_threshold);
LMD_WRAP(calculation_flags);
LMD_WRAP(flags);
LMD_WRAP(stroke_depth_offset);
LMD_WRAP(level_start_override);
LMD_WRAP(level_end_override);
LMD_WRAP(edge_types_override);
LMD_WRAP(shadow_selection_override);
LMD_WRAP(shadow_use_silhouette_override);
LMD_WRAP(cache);
LMD_WRAP(la_data_ptr);
#undef LMD_WRAP
}
void lineart_unwrap_v3(LineartGpencilModifierData *lmd_legacy,
const GreasePencilLineartModifierData *lmd)
{
#define LMD_UNWRAP(var) lmd_legacy->var = lmd->var
LMD_UNWRAP(edge_types);
LMD_UNWRAP(source_type);
LMD_UNWRAP(use_multiple_levels);
LMD_UNWRAP(level_start);
LMD_UNWRAP(level_end);
LMD_UNWRAP(source_camera);
LMD_UNWRAP(light_contour_object);
LMD_UNWRAP(source_object);
LMD_UNWRAP(source_collection);
LMD_UNWRAP(target_material);
STRNCPY(lmd_legacy->source_vertex_group, lmd->source_vertex_group);
STRNCPY(lmd_legacy->vgname, lmd->vgname);
LMD_UNWRAP(overscan);
LMD_UNWRAP(shadow_camera_fov);
LMD_UNWRAP(shadow_camera_size);
LMD_UNWRAP(shadow_camera_near);
LMD_UNWRAP(shadow_camera_far);
LMD_UNWRAP(opacity);
lmd_legacy->thickness = lmd->thickness * 2;
LMD_UNWRAP(mask_switches);
LMD_UNWRAP(material_mask_bits);
LMD_UNWRAP(intersection_mask);
LMD_UNWRAP(shadow_selection);
LMD_UNWRAP(silhouette_selection);
LMD_UNWRAP(crease_threshold);
LMD_UNWRAP(angle_splitting_threshold);
LMD_UNWRAP(chain_smooth_tolerance);
LMD_UNWRAP(chaining_image_threshold);
LMD_UNWRAP(calculation_flags);
LMD_UNWRAP(flags);
LMD_UNWRAP(stroke_depth_offset);
LMD_UNWRAP(level_start_override);
LMD_UNWRAP(level_end_override);
LMD_UNWRAP(edge_types_override);
LMD_UNWRAP(shadow_selection_override);
LMD_UNWRAP(shadow_use_silhouette_override);
LMD_UNWRAP(cache);
LMD_UNWRAP(la_data_ptr);
#undef LMD_UNWRAP
}
} // namespace blender::bke::greasepencil::convert

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

@ -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

@ -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

@ -94,11 +94,5 @@ void BLO_memfile_clear_future(MemFile *memfile);
/* Utilities. */
Main *BLO_memfile_main_get(MemFile *memfile, Main *bmain, Scene **r_scene);
/**
* Saves .blend using undo buffer.
*
* \return success.
*/
bool BLO_memfile_write_file(MemFile *memfile, const char *filepath);
FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction);

View File

@ -175,61 +175,6 @@ Main *BLO_memfile_main_get(MemFile *memfile, Main *bmain, Scene **r_scene)
return bmain_undo;
}
bool BLO_memfile_write_file(MemFile *memfile, const char *filepath)
{
MemFileChunk *chunk;
int file, oflags;
/* NOTE: This is currently used for auto-save and `quit.blend`,
* where _not_ following symbolic-links is OK,
* however if this is ever executed explicitly by the user,
* we may want to allow writing to symbolic-links. */
oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
#ifdef O_NOFOLLOW
/* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */
oflags |= O_NOFOLLOW;
#else
/* TODO(sergey): How to deal with symlinks on windows? */
# ifndef _MSC_VER
# warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103"
# endif
#endif
file = BLI_open(filepath, oflags, 0666);
if (file == -1) {
fprintf(stderr,
"Unable to save '%s': %s\n",
filepath,
errno ? strerror(errno) : "Unknown error opening file");
return false;
}
for (chunk = static_cast<MemFileChunk *>(memfile->chunks.first); chunk;
chunk = static_cast<MemFileChunk *>(chunk->next))
{
#ifdef _WIN32
if (size_t(write(file, chunk->buf, uint(chunk->size))) != chunk->size)
#else
if (size_t(write(file, chunk->buf, chunk->size)) != chunk->size)
#endif
{
break;
}
}
close(file);
if (chunk) {
fprintf(stderr,
"Unable to save '%s': %s\n",
filepath,
errno ? strerror(errno) : "Unknown error writing file");
return false;
}
return true;
}
static int64_t undo_read(FileReader *reader, void *buffer, size_t size)
{
UndoReader *undo = (UndoReader *)reader;

View File

@ -2975,6 +2975,12 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 8)) {
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
light->shadow_filter_radius = 3.0f;
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@ -412,14 +412,20 @@ void MemoryBuffer::add_pixel(int x, int y, const float color[4])
}
}
static void read_ewa_elem(void *userdata, int x, int y, float result[4])
static void read_ewa_elem_checked(void *userdata, int x, int y, float result[4])
{
const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
buffer->read_elem_checked(x, y, result);
}
static void read_ewa_elem_clamped(void *userdata, int x, int y, float result[4])
{
const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
buffer->read_elem_clamped(x, y, result);
}
void MemoryBuffer::read_elem_filtered(
const float x, const float y, float dx[2], float dy[2], float *out) const
const float x, const float y, float dx[2], float dy[2], bool extend_boundary, float *out) const
{
BLI_assert(datatype_ == DataType::Color);
@ -441,7 +447,7 @@ void MemoryBuffer::read_elem_filtered(
uv_normal,
du_normal,
dv_normal,
read_ewa_elem,
extend_boundary ? read_ewa_elem_clamped : read_ewa_elem_checked,
const_cast<MemoryBuffer *>(this),
out);
}

View File

@ -263,7 +263,8 @@ class MemoryBuffer {
}
}
void read_elem_filtered(float x, float y, float dx[2], float dy[2], float *out) const;
void read_elem_filtered(
float x, float y, float dx[2], float dy[2], bool extend_boundary, float *out) const;
/**
* Get channel value at given coordinates.

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

@ -5,6 +5,7 @@
#include "COM_CornerPinNode.h"
#include "COM_PlaneCornerPinOperation.h"
#include "COM_SMAAOperation.h"
namespace blender::compositor {
@ -13,7 +14,36 @@ CornerPinNode::CornerPinNode(bNode *editor_node) : Node(editor_node) {}
void CornerPinNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
NodeInput *input_image = this->get_input_socket(0);
PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation();
converter.add_operation(plane_mask_operation);
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_edge_detection->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(1), smaa_neighborhood->get_output_socket());
PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation();
converter.add_operation(warp_image_operation);
converter.map_input_socket(this->get_input_socket(0), warp_image_operation->get_input_socket(0));
/* NOTE: socket order differs between UI node and operations:
* bNode uses intuitive order following top-down layout:
* upper-left, upper-right, lower-left, lower-right
@ -21,23 +51,20 @@ void CornerPinNode::convert_to_operations(NodeConverter &converter,
* lower-left, lower-right, upper-right, upper-left
*/
const int node_corner_index[4] = {3, 4, 2, 1};
NodeOutput *output_warped_image = this->get_output_socket(0);
NodeOutput *output_plane = this->get_output_socket(1);
PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation();
converter.add_operation(warp_image_operation);
PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation();
converter.add_operation(plane_mask_operation);
converter.map_input_socket(input_image, warp_image_operation->get_input_socket(0));
for (int i = 0; i < 4; i++) {
NodeInput *corner_input = get_input_socket(node_corner_index[i]);
converter.map_input_socket(corner_input, warp_image_operation->get_input_socket(i + 1));
converter.map_input_socket(corner_input, plane_mask_operation->get_input_socket(i));
}
converter.map_output_socket(output_warped_image, warp_image_operation->get_output_socket());
converter.map_output_socket(output_plane, plane_mask_operation->get_output_socket());
SetAlphaMultiplyOperation *set_alpha_operation = new SetAlphaMultiplyOperation();
converter.add_operation(set_alpha_operation);
converter.add_link(warp_image_operation->get_output_socket(),
set_alpha_operation->get_input_socket(0));
converter.add_link(smaa_neighborhood->get_output_socket(),
set_alpha_operation->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(0),
set_alpha_operation->get_output_socket());
}
} // namespace blender::compositor

View File

@ -5,6 +5,7 @@
#include "COM_PlaneTrackDeformNode.h"
#include "COM_PlaneTrackOperation.h"
#include "COM_SMAAOperation.h"
namespace blender::compositor {
@ -22,9 +23,39 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
int frame_number = context.get_framenumber();
NodeInput *input_image = this->get_input_socket(0);
NodeOutput *output_warped_image = this->get_output_socket(0);
NodeOutput *output_plane = this->get_output_socket(1);
PlaneTrackMaskOperation *plane_mask_operation = new PlaneTrackMaskOperation();
plane_mask_operation->set_movie_clip(clip);
plane_mask_operation->set_tracking_object(data->tracking_object);
plane_mask_operation->set_plane_track_name(data->plane_track_name);
plane_mask_operation->set_framenumber(frame_number);
if (data->flag & CMP_NODE_PLANE_TRACK_DEFORM_FLAG_MOTION_BLUR) {
plane_mask_operation->set_motion_blur_samples(data->motion_blur_samples);
plane_mask_operation->set_motion_blur_shutter(data->motion_blur_shutter);
}
converter.add_operation(plane_mask_operation);
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_edge_detection->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(1), smaa_neighborhood->get_output_socket());
PlaneTrackWarpImageOperation *warp_image_operation = new PlaneTrackWarpImageOperation();
warp_image_operation->set_movie_clip(clip);
@ -37,21 +68,16 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
}
converter.add_operation(warp_image_operation);
converter.map_input_socket(input_image, warp_image_operation->get_input_socket(0));
converter.map_output_socket(output_warped_image, warp_image_operation->get_output_socket());
converter.map_input_socket(this->get_input_socket(0), warp_image_operation->get_input_socket(0));
PlaneTrackMaskOperation *plane_mask_operation = new PlaneTrackMaskOperation();
plane_mask_operation->set_movie_clip(clip);
plane_mask_operation->set_tracking_object(data->tracking_object);
plane_mask_operation->set_plane_track_name(data->plane_track_name);
plane_mask_operation->set_framenumber(frame_number);
if (data->flag & CMP_NODE_PLANE_TRACK_DEFORM_FLAG_MOTION_BLUR) {
plane_mask_operation->set_motion_blur_samples(data->motion_blur_samples);
plane_mask_operation->set_motion_blur_shutter(data->motion_blur_shutter);
}
converter.add_operation(plane_mask_operation);
converter.map_output_socket(output_plane, plane_mask_operation->get_output_socket());
SetAlphaMultiplyOperation *set_alpha_operation = new SetAlphaMultiplyOperation();
converter.add_operation(set_alpha_operation);
converter.add_link(warp_image_operation->get_output_socket(),
set_alpha_operation->get_input_socket(0));
converter.add_link(smaa_neighborhood->get_output_socket(),
set_alpha_operation->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(0),
set_alpha_operation->get_output_socket());
}
} // namespace blender::compositor

View File

@ -152,7 +152,7 @@ void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
else {
/* EWA filtering (without nearest it gets blurry with NO distortion). */
input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], false, it.out);
}
}
}

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

@ -144,7 +144,7 @@ void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
else {
/* EWA filtering. */
input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], false, it.out);
/* UV to alpha threshold. */
const float threshold = alpha_ * 0.05f;

View File

@ -99,7 +99,7 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer *
if (motion_blur_samples_ == 1) {
for (; !it.is_end(); ++it) {
warp_coord(it.x, it.y, samples_[0].perspective_matrix, uv, deriv);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], true, it.out);
}
}
else {
@ -108,7 +108,7 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer *
for (const int sample : IndexRange(motion_blur_samples_)) {
float color[4];
warp_coord(it.x, it.y, samples_[sample].perspective_matrix, uv, deriv);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], color);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], true, color);
add_v4_v4(it.out, color);
}
mul_v4_fl(it.out, 1.0f / float(motion_blur_samples_));
@ -175,14 +175,6 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx,
PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation()
{
add_output_socket(DataType::Value);
/* Currently hardcoded to 8 samples. */
osa_ = 8;
}
void PlaneDistortMaskOperation::init_execution()
{
BLI_jitter_init(jitter_, osa_);
}
void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
@ -190,37 +182,22 @@ void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *outpu
Span<MemoryBuffer *> /*inputs*/)
{
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
int inside_count = 0;
float accumulated_mask = 0.0f;
const float2 point = float2(it.x, it.y);
for (const int motion_sample : IndexRange(motion_blur_samples_)) {
MotionSample &sample = samples_[motion_sample];
inside_count += get_jitter_samples_inside_count(it.x, it.y, sample);
const bool is_inside_plane = isect_point_tri_v2(point,
sample.frame_space_corners[0],
sample.frame_space_corners[1],
sample.frame_space_corners[2]) ||
isect_point_tri_v2(point,
sample.frame_space_corners[0],
sample.frame_space_corners[2],
sample.frame_space_corners[3]);
accumulated_mask += is_inside_plane ? 1.0f : 0.0f;
}
*it.out = float(inside_count) / (osa_ * motion_blur_samples_);
*it.out = accumulated_mask / motion_blur_samples_;
}
}
int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x,
int y,
MotionSample &sample_data)
{
float point[2];
int inside_count = 0;
for (int sample = 0; sample < osa_; sample++) {
point[0] = x + jitter_[sample][0];
point[1] = y + jitter_[sample][1];
if (isect_point_tri_v2(point,
sample_data.frame_space_corners[0],
sample_data.frame_space_corners[1],
sample_data.frame_space_corners[2]) ||
isect_point_tri_v2(point,
sample_data.frame_space_corners[0],
sample_data.frame_space_corners[2],
sample_data.frame_space_corners[3]))
{
inside_count++;
}
}
return inside_count;
}
} // namespace blender::compositor

View File

@ -60,21 +60,12 @@ class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation {
};
class PlaneDistortMaskOperation : public PlaneDistortBaseOperation {
protected:
int osa_;
float jitter_[32][2];
public:
PlaneDistortMaskOperation();
void init_execution() override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
private:
int get_jitter_samples_inside_count(int x, int y, MotionSample &sample_data);
};
} // namespace blender::compositor

View File

@ -191,7 +191,9 @@ set(GLSL_SRC
shaders/compositor_parallel_reduction.glsl
shaders/compositor_pixelate.glsl
shaders/compositor_plane_deform.glsl
shaders/compositor_plane_deform_mask.glsl
shaders/compositor_plane_deform_motion_blur.glsl
shaders/compositor_plane_deform_motion_blur_mask.glsl
shaders/compositor_premultiply_alpha.glsl
shaders/compositor_projector_lens_distortion.glsl
shaders/compositor_read_input.glsl
@ -315,7 +317,6 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/compositor_parallel_reduction_info.hh
shaders/infos/compositor_pixelate_info.hh
shaders/infos/compositor_plane_deform_info.hh
shaders/infos/compositor_plane_deform_motion_blur_info.hh
shaders/infos/compositor_premultiply_alpha_info.hh
shaders/infos/compositor_projector_lens_distortion_info.hh
shaders/infos/compositor_read_input_info.hh

View File

@ -8,8 +8,6 @@ void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
/* Add 0.5 to evaluate the input sampler at the center of the pixel and divide by the image size
* to get the coordinates into the sampler's expected [0, 1] range. */
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(texture_size(input_tx));
vec3 transformed_coordinates = mat3(homography_matrix) * vec3(coordinates, 1.0);
@ -23,14 +21,8 @@ void main()
vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient);
/* The plane mask is 1 if it is inside the plane and 0 otherwise. However, we use the alpha value
* of the sampled color for pixels outside of the plane to utilize the anti-aliasing effect of
* the anisotropic filtering. Therefore, the input_tx sampler should use anisotropic filtering
* and be clamped to zero border color. */
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
float mask_value = is_inside_plane ? 1.0 : sampled_color.a;
/* Premultiply the mask value as an alpha. */
vec4 plane_color = sampled_color * texture_load(mask_tx, texel).x;
imageStore(output_img, texel, sampled_color);
imageStore(mask_img, texel, vec4(mask_value));
imageStore(output_img, texel, plane_color);
}

View File

@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(mask_img));
vec3 transformed_coordinates = mat3(homography_matrix) * vec3(coordinates, 1.0);
vec2 projected_coordinates = transformed_coordinates.xy / transformed_coordinates.z;
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
float mask_value = is_inside_plane ? 1.0 : 0.0;
imageStore(mask_img, texel, vec4(mask_value));
}

View File

@ -2,17 +2,14 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
/* Add 0.5 to evaluate the sampler at the center of the pixel and divide by the size to get the
* coordinates into the sampler's expected [0, 1] range. We choose the maximum between both
* output sizes because one of the outputs might be a dummy 1x1 image. */
ivec2 output_size = max(imageSize(output_img), imageSize(mask_img));
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(output_size);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(output_img));
float accumulated_mask = 0.0;
vec4 accumulated_color = vec4(0.0);
for (int i = 0; i < number_of_motion_blur_samples; i++) {
mat3 homography_matrix = mat3(homography_matrices[i]);
@ -28,19 +25,12 @@ void main()
vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient);
accumulated_color += sampled_color;
/* The plane mask is 1 if it is inside the plane and 0 otherwise. However, we use the alpha
* value of the sampled color for pixels outside of the plane to utilize the anti-aliasing
* effect of the anisotropic filtering. Therefore, the input_tx sampler should use anisotropic
* filtering and be clamped to zero border color. */
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
accumulated_mask += is_inside_plane ? 1.0 : sampled_color.a;
}
accumulated_mask /= number_of_motion_blur_samples;
accumulated_color /= number_of_motion_blur_samples;
imageStore(output_img, texel, accumulated_color);
imageStore(mask_img, texel, vec4(accumulated_mask));
/* Premultiply the mask value as an alpha. */
vec4 plane_color = accumulated_color * texture_load(mask_tx, texel).x;
imageStore(output_img, texel, plane_color);
}

View File

@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(mask_img));
float accumulated_mask = 0.0;
for (int i = 0; i < number_of_motion_blur_samples; i++) {
mat3 homography_matrix = mat3(homography_matrices[i]);
vec3 transformed_coordinates = homography_matrix * vec3(coordinates, 1.0);
vec2 projected_coordinates = transformed_coordinates.xy / transformed_coordinates.z;
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
accumulated_mask += is_inside_plane ? 1.0 : 0.0;
}
accumulated_mask /= number_of_motion_blur_samples;
imageStore(mask_img, texel, vec4(accumulated_mask));
}

View File

@ -4,11 +4,36 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_plane_deform_mask)
.local_group_size(16, 16)
.push_constant(Type::MAT4, "homography_matrix")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform_mask.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_plane_deform)
.local_group_size(16, 16)
.push_constant(Type::MAT4, "homography_matrix")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.sampler(1, ImageType::FLOAT_2D, "mask_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_plane_deform_motion_blur_mask)
.local_group_size(16, 16)
.push_constant(Type::INT, "number_of_motion_blur_samples")
.uniform_buf(0, "mat4", "homography_matrices[64]")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform_motion_blur_mask.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_plane_deform_motion_blur)
.local_group_size(16, 16)
.push_constant(Type::INT, "number_of_motion_blur_samples")
.uniform_buf(0, "mat4", "homography_matrices[64]")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.sampler(1, ImageType::FLOAT_2D, "mask_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.compute_source("compositor_plane_deform_motion_blur.glsl")
.do_static_compilation(true);

View File

@ -1,15 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_plane_deform_motion_blur)
.local_group_size(16, 16)
.push_constant(Type::INT, "number_of_motion_blur_samples")
.uniform_buf(0, "mat4", "homography_matrices[64]")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform_motion_blur.glsl")
.do_static_compilation(true);

View File

@ -78,6 +78,8 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
this->power[LIGHT_SPECULAR] = la->spec_fac * shape_power;
this->power[LIGHT_VOLUME] = la->volume_fac * point_power;
this->pcf_radius = la->shadow_filter_radius;
eLightType new_type = to_light_type(la->type, la->area_shape, la->mode & LA_USE_SOFT_FALLOFF);
if (assign_if_different(this->type, new_type)) {
shadow_discard_safe(shadows);

View File

@ -807,8 +807,6 @@ struct LightData {
int tilemap_index;
/** Directional : Offset of the LOD min in LOD min tile units. */
int2 clipmap_base_offset;
/** Number of step for shadow map tracing. */
int shadow_ray_step_count;
/** Punctual: Other parts of the perspective matrix. */
float clip_side;
/** Punctual: Shift to apply to the light origin to get the shadow projection origin. */
@ -817,7 +815,9 @@ struct LightData {
float shadow_shape_scale_or_angle;
/** Trace distance for directional lights. */
float shadow_trace_distance;
float _pad2;
/* Radius in pixels for shadow filtering. */
float pcf_radius;
int _pad0;
};
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
@ -1024,7 +1024,7 @@ struct ShadowSceneData {
int step_count;
/* Bias the shading point by using the normal to avoid self intersection. */
float normal_bias;
int _pad2;
int _pad0;
};
BLI_STATIC_ASSERT_ALIGN(ShadowSceneData, 16)

View File

@ -1008,28 +1008,6 @@ void ShadowModule::end_sync()
{
Manager &manager = *inst_.manager;
{
/* Mark for update all shadow pages touching an updated shadow caster. */
PassSimple &pass = caster_update_ps_;
pass.init();
pass.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_UPDATE));
pass.bind_ssbo("tilemaps_buf", tilemap_pool.tilemaps_data);
pass.bind_ssbo("tiles_buf", tilemap_pool.tiles_data);
/* Past caster transforms. */
if (past_casters_updated_.size() > 0) {
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.previous());
pass.bind_ssbo("resource_ids_buf", past_casters_updated_);
pass.dispatch(int3(past_casters_updated_.size(), 1, tilemap_pool.tilemaps_data.size()));
}
/* Current caster transforms. */
if (curr_casters_updated_.size() > 0) {
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
pass.bind_ssbo("resource_ids_buf", curr_casters_updated_);
pass.dispatch(int3(curr_casters_updated_.size(), 1, tilemap_pool.tilemaps_data.size()));
}
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
}
{
PassSimple &pass = tilemap_setup_ps_;
pass.init();
@ -1079,6 +1057,28 @@ void ShadowModule::end_sync()
}
}
{
/* Mark for update all shadow pages touching an updated shadow caster. */
PassSimple &pass = caster_update_ps_;
pass.init();
pass.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_UPDATE));
pass.bind_ssbo("tilemaps_buf", tilemap_pool.tilemaps_data);
pass.bind_ssbo("tiles_buf", tilemap_pool.tiles_data);
/* Past caster transforms. */
if (past_casters_updated_.size() > 0) {
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.previous());
pass.bind_ssbo("resource_ids_buf", past_casters_updated_);
pass.dispatch(int3(past_casters_updated_.size(), 1, tilemap_pool.tilemaps_data.size()));
}
/* Current caster transforms. */
if (curr_casters_updated_.size() > 0) {
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
pass.bind_ssbo("resource_ids_buf", curr_casters_updated_);
pass.dispatch(int3(curr_casters_updated_.size(), 1, tilemap_pool.tilemaps_data.size()));
}
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
}
/* Non volume usage tagging happens between these two steps.
* (Setup at begin_sync) */
@ -1337,13 +1337,7 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
}
inst_.hiz_buffer.update();
/* Run caster update once and before the update loop.
* This is valid even before the view update since only the static tilemaps
* are concerned about this tagging. */
/* TODO(fclem): There is an optimization opportunity here where we can
* test casters only against the static tilemaps instead of all of them. */
inst_.manager->submit(caster_update_ps_, view);
bool update_casters = true;
do {
DRW_stats_group_start("Shadow");
@ -1351,6 +1345,12 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
GPU_uniformbuf_clear_to_zero(shadow_multi_view_.matrices_ubo_get());
inst_.manager->submit(tilemap_setup_ps_, view);
if (assign_if_different(update_casters, false)) {
/* Run caster update only once. */
/* TODO(fclem): There is an optimization opportunity here where we can
* test casters only against the static tilemaps instead of all of them. */
inst_.manager->submit(caster_update_ps_, view);
}
inst_.manager->submit(tilemap_usage_ps_, view);
inst_.manager->submit(tilemap_update_ps_, view);

View File

@ -14,20 +14,39 @@
# define SHADOW_ATLAS_TYPE usampler2DArray
#endif
float shadow_read_depth_at_tilemap_uv(SHADOW_ATLAS_TYPE atlas_tx,
usampler2D tilemaps_tx,
int tilemap_index,
vec2 tilemap_uv)
struct ShadowSampleParams {
vec3 lP;
vec3 uv;
int tilemap_index;
float z_range;
};
ShadowTileData shadow_tile_data_get(usampler2D tilemaps_tx, ShadowSampleParams params)
{
/* Prevent out of bound access. Assumes the input is already non negative. */
tilemap_uv = min(tilemap_uv, vec2(0.99999));
vec2 tilemap_uv = min(params.uv.xy, vec2(0.99999));
ivec2 texel_coord = ivec2(tilemap_uv * float(SHADOW_MAP_MAX_RES));
/* Using bitwise ops is way faster than integer ops. */
const int page_shift = SHADOW_PAGE_LOD;
ivec2 tile_coord = texel_coord >> page_shift;
ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_coord, tilemap_index);
return shadow_tile_load(tilemaps_tx, tile_coord, params.tilemap_index);
}
float shadow_read_depth(SHADOW_ATLAS_TYPE atlas_tx,
usampler2D tilemaps_tx,
ShadowSampleParams params)
{
/* Prevent out of bound access. Assumes the input is already non negative. */
vec2 tilemap_uv = min(params.uv.xy, vec2(0.99999));
ivec2 texel_coord = ivec2(tilemap_uv * float(SHADOW_MAP_MAX_RES));
/* Using bitwise ops is way faster than integer ops. */
const int page_shift = SHADOW_PAGE_LOD;
ivec2 tile_coord = texel_coord >> page_shift;
ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_coord, params.tilemap_index);
if (!tile.is_allocated) {
return -1.0;
@ -51,25 +70,36 @@ struct ShadowEvalResult {
/** \name Shadow Sampling Functions
* \{ */
/* TODO(fclem): Remove. Only here to avoid include order hell with common_math_lib. */
mat4x4 shadow_projection_perspective(
float left, float right, float bottom, float top, float near_clip, float far_clip)
mat4x4 shadow_projection_perspective(float side, float near_clip, float far_clip)
{
float x_delta = right - left;
float y_delta = top - bottom;
float z_delta = far_clip - near_clip;
mat4x4 mat = mat4x4(1.0);
if (x_delta != 0.0 && y_delta != 0.0 && z_delta != 0.0) {
mat[0][0] = near_clip * 2.0 / x_delta;
mat[1][1] = near_clip * 2.0 / y_delta;
mat[2][0] = (right + left) / x_delta; /* NOTE: negate Z. */
mat[2][1] = (top + bottom) / y_delta;
mat[2][2] = -(far_clip + near_clip) / z_delta;
mat[2][3] = -1.0;
mat[3][2] = (-2.0 * near_clip * far_clip) / z_delta;
mat[3][3] = 0.0;
}
mat[0][0] = near_clip / side;
mat[1][1] = near_clip / side;
mat[2][0] = 0.0;
mat[2][1] = 0.0;
mat[2][2] = -(far_clip + near_clip) / z_delta;
mat[2][3] = -1.0;
mat[3][2] = (-2.0 * near_clip * far_clip) / z_delta;
mat[3][3] = 0.0;
return mat;
}
mat4x4 shadow_projection_perspective_inverse(float side, float near_clip, float far_clip)
{
float z_delta = far_clip - near_clip;
float d = 2.0 * near_clip * far_clip;
mat4x4 mat = mat4x4(1.0);
mat[0][0] = side / near_clip;
mat[1][1] = side / near_clip;
mat[2][0] = 0.0;
mat[2][1] = 0.0;
mat[2][2] = 0.0;
mat[2][3] = (near_clip - far_clip) / d;
mat[3][2] = -1.0;
mat[3][3] = (near_clip + far_clip) / d;
return mat;
}
@ -92,33 +122,142 @@ float shadow_linear_occluder_distance(LightData light,
return receiver_z - occluder_z;
}
ShadowEvalResult shadow_punctual_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
usampler2D tilemaps_tx,
LightData light,
vec3 P)
mat4 shadow_punctual_projection_perspective(LightData light)
{
/* Face Local (View) Space > Clip Space. */
float clip_far = intBitsToFloat(light.clip_far);
float clip_near = intBitsToFloat(light.clip_near);
float clip_side = light.clip_side;
return shadow_projection_perspective(clip_side, clip_near, clip_far);
}
mat4 shadow_punctual_projection_perspective_inverse(LightData light)
{
/* Face Local (View) Space > Clip Space. */
float clip_far = intBitsToFloat(light.clip_far);
float clip_near = intBitsToFloat(light.clip_near);
float clip_side = light.clip_side;
return shadow_projection_perspective_inverse(clip_side, clip_near, clip_far);
}
vec3 shadow_punctual_reconstruct_position(ShadowSampleParams params,
mat4 wininv,
LightData light,
vec3 uvw)
{
vec3 clip_P = uvw * 2.0 - 1.0;
vec3 lP = project_point(wininv, clip_P);
int face_id = params.tilemap_index - light.tilemap_index;
lP = shadow_punctual_face_local_to_local_position(face_id, lP);
return mat3(light.object_mat) * lP + light._position;
}
ShadowSampleParams shadow_punctual_sample_params_get(usampler2D tilemaps_tx,
LightData light,
vec3 P)
{
vec3 lP = (P - light._position) * mat3(light.object_mat);
int face_id = shadow_punctual_face_index_get(lP);
/* Local Light Space > Face Local (View) Space. */
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
/* Face Local (View) Space > Clip Space. */
float clip_far = intBitsToFloat(light.clip_far);
float clip_near = intBitsToFloat(light.clip_near);
float clip_side = light.clip_side;
/* TODO: Could be simplified since frustum is completely symmetrical. */
mat4 winmat = shadow_projection_perspective(
-clip_side, clip_side, -clip_side, clip_side, clip_near, clip_far);
mat4 winmat = shadow_punctual_projection_perspective(light);
vec3 clip_P = project_point(winmat, lP);
/* Clip Space > UV Space. */
vec3 uv_P = saturate(clip_P * 0.5 + 0.5);
float depth = shadow_read_depth_at_tilemap_uv(
atlas_tx, tilemaps_tx, light.tilemap_index + face_id, uv_P.xy);
ShadowSampleParams result;
result.lP = lP;
result.uv = uv_P;
result.tilemap_index = light.tilemap_index + face_id;
result.z_range = 1.0;
return result;
}
ShadowEvalResult shadow_punctual_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
usampler2D tilemaps_tx,
LightData light,
vec3 P)
{
ShadowSampleParams params = shadow_punctual_sample_params_get(tilemaps_tx, light, P);
float depth = shadow_read_depth(atlas_tx, tilemaps_tx, params);
ShadowEvalResult result;
result.light_visibilty = float(uv_P.z < depth);
result.occluder_distance = shadow_linear_occluder_distance(light, false, lP, depth);
result.light_visibilty = float(params.uv.z < depth);
result.occluder_distance = shadow_linear_occluder_distance(light, false, params.lP, depth);
return result;
}
struct ShadowDirectionalSampleInfo {
float clip_near;
float clip_far;
int level_relative;
int lod_relative;
ivec2 clipmap_offset;
vec2 clipmap_origin;
};
ShadowDirectionalSampleInfo shadow_directional_sample_info_get(LightData light, vec3 lP)
{
ShadowDirectionalSampleInfo info;
info.clip_near = orderedIntBitsToFloat(light.clip_near);
info.clip_far = orderedIntBitsToFloat(light.clip_far);
int level = shadow_directional_level(light, lP - light._position);
/* This difference needs to be less than 32 for the later shift to be valid.
* This is ensured by ShadowDirectional::clipmap_level_range(). */
info.level_relative = level - light.clipmap_lod_min;
info.lod_relative = (light.type == LIGHT_SUN_ORTHO) ? light.clipmap_lod_min : level;
info.clipmap_offset = shadow_decompress_grid_offset(
light.type, light.clipmap_base_offset, info.level_relative);
info.clipmap_origin = vec2(light._clipmap_origin_x, light._clipmap_origin_y);
return info;
}
vec3 shadow_directional_reconstruct_position(ShadowSampleParams params, LightData light, vec3 uvw)
{
ShadowDirectionalSampleInfo info = shadow_directional_sample_info_get(light, params.lP);
vec2 tilemap_uv = uvw.xy;
tilemap_uv += vec2(info.clipmap_offset) / float(SHADOW_TILEMAP_RES);
vec2 clipmap_pos = (tilemap_uv - 0.5) / exp2(-float(info.lod_relative));
vec3 lP;
lP.xy = clipmap_pos + info.clipmap_origin;
lP.z = (params.uv.z + info.clip_near) * -1.0;
return mat3(light.object_mat) * lP;
}
ShadowSampleParams shadow_directional_sample_params_get(usampler2D tilemaps_tx,
LightData light,
vec3 P)
{
vec3 lP = P * mat3(light.object_mat);
ShadowDirectionalSampleInfo info = shadow_directional_sample_info_get(light, lP);
ShadowCoordinates coord = shadow_directional_coordinates(light, lP);
/* Assumed to be non-null. */
float z_range = info.clip_far - info.clip_near;
float dist_to_near_plane = -lP.z - info.clip_near;
vec2 clipmap_pos = lP.xy - info.clipmap_origin;
vec2 tilemap_uv = clipmap_pos * exp2(-float(info.lod_relative)) + 0.5;
/* Translate tilemap UVs to its origin. */
tilemap_uv -= vec2(info.clipmap_offset) / float(SHADOW_TILEMAP_RES);
/* Clamp to avoid out of tilemap access. */
tilemap_uv = saturate(tilemap_uv);
ShadowSampleParams result;
result.lP = lP;
result.uv = vec3(tilemap_uv, dist_to_near_plane);
result.tilemap_index = light.tilemap_index + info.level_relative;
result.z_range = z_range;
return result;
}
@ -127,40 +266,13 @@ ShadowEvalResult shadow_directional_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
LightData light,
vec3 P)
{
vec3 lP = P * mat3(light.object_mat);
ShadowCoordinates coord = shadow_directional_coordinates(light, lP);
ShadowSampleParams params = shadow_directional_sample_params_get(tilemaps_tx, light, P);
float clip_near = orderedIntBitsToFloat(light.clip_near);
float clip_far = orderedIntBitsToFloat(light.clip_far);
/* Assumed to be non-null. */
float z_range = clip_far - clip_near;
float dist_to_near_plane = -lP.z - clip_near;
int level = shadow_directional_level(light, lP - light._position);
/* This difference needs to be less than 32 for the later shift to be valid.
* This is ensured by ShadowDirectional::clipmap_level_range(). */
int level_relative = level - light.clipmap_lod_min;
int lod_relative = (light.type == LIGHT_SUN_ORTHO) ? light.clipmap_lod_min : level;
vec2 clipmap_origin = vec2(light._clipmap_origin_x, light._clipmap_origin_y);
vec2 clipmap_pos = lP.xy - clipmap_origin;
vec2 tilemap_uv = clipmap_pos * exp2(-float(lod_relative)) + 0.5;
/* Compute offset in tile. */
ivec2 clipmap_offset = shadow_decompress_grid_offset(
light.type, light.clipmap_base_offset, level_relative);
/* Translate tilemap UVs to its origin. */
tilemap_uv -= vec2(clipmap_offset) / float(SHADOW_TILEMAP_RES);
/* Clamp to avoid out of tilemap access. */
tilemap_uv = saturate(tilemap_uv);
float depth = shadow_read_depth_at_tilemap_uv(
atlas_tx, tilemaps_tx, light.tilemap_index + level_relative, tilemap_uv);
float depth = shadow_read_depth(atlas_tx, tilemaps_tx, params);
ShadowEvalResult result;
result.light_visibilty = float(dist_to_near_plane < depth * z_range);
result.occluder_distance = shadow_linear_occluder_distance(light, true, lP, depth);
result.light_visibilty = float(params.uv.z < depth * params.z_range);
result.occluder_distance = shadow_linear_occluder_distance(light, true, params.lP, depth);
return result;
}

View File

@ -205,6 +205,24 @@ vec3 shadow_punctual_local_position_to_face_local(int face_id, vec3 lL)
}
}
vec3 shadow_punctual_face_local_to_local_position(int face_id, vec3 fL)
{
switch (face_id) {
case 1:
return vec3(-fL.z, -fL.x, fL.y);
case 2:
return vec3(fL.z, fL.x, fL.y);
case 3:
return vec3(fL.x, -fL.z, fL.y);
case 4:
return vec3(-fL.x, fL.z, fL.y);
case 5:
return vec3(fL.x, -fL.y, -fL.z);
default:
return fL;
}
}
/* Turns local light coordinate into shadow region index. Matches eCubeFace order.
* \note lL does not need to be normalized. */
int shadow_punctual_face_index_get(vec3 lL)

View File

@ -12,6 +12,8 @@
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_bxdf_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
#pragma BLENDER_REQUIRE(draw_math_geom_lib.glsl)
float shadow_read_depth_at_tilemap_uv(int tilemap_index, vec2 tilemap_uv)
{
@ -406,6 +408,68 @@ SHADOW_MAP_TRACE_FN(ShadowRayPunctual)
/** \name Shadow Evaluation
* \{ */
/* Compute the world space offset of the shading position required for
* stochastic percentage closer filtering of shadow-maps. */
vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3 Ng)
{
if (light.pcf_radius <= 0.001) {
/* Early return. */
return vec3(0.0);
}
ShadowSampleParams params;
if (is_directional) {
params = shadow_directional_sample_params_get(shadow_tilemaps_tx, light, P);
}
else {
params = shadow_punctual_sample_params_get(shadow_tilemaps_tx, light, P);
}
ShadowTileData tile = shadow_tile_data_get(shadow_tilemaps_tx, params);
if (!tile.is_allocated) {
return vec3(0.0);
}
/* Compute the shadow-map tangent-bitangent matrix. */
float uv_offset = 1.0 / float(SHADOW_MAP_MAX_RES);
vec3 TP, BP;
if (is_directional) {
TP = shadow_directional_reconstruct_position(
params, light, params.uv + vec3(uv_offset, 0.0, 0.0));
BP = shadow_directional_reconstruct_position(
params, light, params.uv + vec3(0.0, uv_offset, 0.0));
vec3 L = light._back;
/* Project the offset positions into the surface plane. */
TP = line_plane_intersect(TP, dot(L, TP) > 0.0 ? L : -L, P, Ng);
BP = line_plane_intersect(BP, dot(L, BP) > 0.0 ? L : -L, P, Ng);
}
else {
mat4 wininv = shadow_punctual_projection_perspective_inverse(light);
TP = shadow_punctual_reconstruct_position(
params, wininv, light, params.uv + vec3(uv_offset, 0.0, 0.0));
BP = shadow_punctual_reconstruct_position(
params, wininv, light, params.uv + vec3(0.0, uv_offset, 0.0));
/* Project the offset positions into the surface plane. */
TP = line_plane_intersect(light._position, normalize(TP - light._position), P, Ng);
BP = line_plane_intersect(light._position, normalize(BP - light._position), P, Ng);
}
/* TODO: Use a mat2x3 (Currently not supported by the Metal backend). */
mat3 TBN = mat3(TP - P, BP - P, Ng);
/* Compute the actual offset. */
vec2 rand = vec2(0.0);
#ifdef EEVEE_SAMPLING_DATA
rand = sampling_rng_2D_get(SAMPLING_SHADOW_V);
#endif
vec2 pcf_offset = interlieved_gradient_noise(UTIL_TEXEL, vec2(0.0), rand);
pcf_offset = pcf_offset * 2.0 - 1.0;
pcf_offset *= light.pcf_radius;
return TBN * vec3(pcf_offset, 0.0);
}
/**
* Evaluate shadowing by casting rays toward the light direction.
*/
@ -433,6 +497,8 @@ ShadowEvalResult shadow_eval(LightData light,
float normal_offset = 0.02;
#endif
P += shadow_pcf_offset(light, is_directional, P, Ng);
/* Avoid self intersection. */
P = offset_ray(P, Ng);
/* The above offset isn't enough in most situation. Still add a bigger bias. */

View File

@ -44,7 +44,7 @@ static void add_values_to_text_cache(const GVArray &values,
const float3 position = math::transform_point(object_to_world, positions[i]);
const T &value = values_typed[i];
char numstr[32];
char numstr[64];
size_t numstr_len = 0;
if constexpr (std::is_same_v<T, bool>) {
numstr_len = SNPRINTF_RLEN(numstr, "%s", value ? "True" : "False");
@ -77,7 +77,8 @@ static void add_values_to_text_cache(const GVArray &values,
numstr, "(%.3f, %.3f, %.3f, %.3f)", value.r, value.g, value.b, value.a);
}
else if constexpr (std::is_same_v<T, math::Quaternion>) {
numstr_len = SNPRINTF_RLEN(numstr, "(%g, %g, %g, %g)", value.w, value.x, value.y, value.z);
numstr_len = SNPRINTF_RLEN(
numstr, "(%.3f, %.3f, %.3f, %.3f)", value.w, value.x, value.y, value.z);
}
else {
BLI_assert_unreachable();

View File

@ -136,10 +136,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

@ -129,7 +129,7 @@ void ED_undosys_type_free();
/* `memfile_undo.cc` */
MemFile *ED_undosys_stack_memfile_get_active(UndoStack *ustack);
MemFile *ED_undosys_stack_memfile_get_if_active(UndoStack *ustack);
/**
* If the last undo step is a memfile one, find the first #MemFileChunk matching given ID
* (using its session UUID), and tag it as "changed in the future".

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